////
When moving to quarkus, consider headers an include directives below.
////
:quarkus-guides-url: https://quarkus.io/guides
= Drools Quarkus extension

// include::./attributes.adoc[]

This guide demonstrates how your Quarkus application can use https://www.drools.org[Drools] to add intelligent automation
and power it up with the Drools rule engine.

https://www.drools.org[Drools] is a set of projects focusing on intelligent automation and decision management, most notably providing a forward-chaining and backward-chaining inference-based rule engine, DMN decisions engine and other projects. A rule engine is a fundamental building block to create an expert system which, in artificial intelligence, is a computer system that emulates the decision-making ability of a human expert. You can read more information on the https://www.drools.org[Drools website].

== Prerequisites

To complete this guide, you need:

* less than 15 minutes
* an IDE
* JDK 11+ installed with `JAVA_HOME` configured appropriately
* Apache Maven 3.8.6+
* Docker
* link:{quarkus-guides-url}/building-native-image[GraalVM installed if you want to run in native mode]

== Architecture

In this example, we will augment a basic Quarkus application with a few simple rules to infer potential issues with the status of a home automation setup.

We will define a Drools Rule Unit and the rules in the DRL format.

We will wire the Rule Unit into a standard Quarkus CDI bean, for use in the Quarkus application (for instance, wiring MQTT messages from Kafka, etc.).

== Solution

We recommend that you follow the instructions in the next sections and create the application step by step.

However, you can go explore the original example at the following link: https://github.com/kiegroup/drools/tree/main/drools-drl-quarkus-extension/drools-drl-quarkus-quickstart-test

== Creating the Maven Project

First, we need a new Quarkus project.
To create a new Quarkus project, you can reference the link:{quarkus-guides-url}[Quarkus Guide]

When you have your Quarkus project configured, you can add the Drools Quarkus extensions to your project by adding the following dependencies to your `pom.xml`:

[source,xml,subs=attributes+]
----
<dependency>
    <groupId>org.drools</groupId>
    <artifactId>drools-drl-quarkus</artifactId>
</dependency>
<dependency>
    <groupId>org.drools</groupId>
    <artifactId>drools-ruleunits-engine</artifactId>
</dependency>

<dependency>
    <groupId>org.assertj</groupId>
    <artifactId>assertj-core</artifactId>
    <scope>test</scope>
</dependency>
----

== Writing the application

Let's start from the application domain model.

This application goal is to infer potential issues with the status of a home automation setup, so we create the necessary domain models to represent status of sensors, devices and other things inside the house.

Light device domain model:

[source,java]
----
package org.drools.quarkus.quickstart.test.model;

public class Light {
    private final String name;
    private Boolean powered;
    
	public Light(String name, Boolean powered) {
		this.name = name;
		this.powered = powered;
	}

    // getters, setters, etc.
}
----

CCTV security camera domain model:

[source,java]
----
package org.drools.quarkus.quickstart.test.model;

public class CCTV {
    private final String name;
    private Boolean powered;

    public CCTV(String name, Boolean powered) {
        this.name = name;
        this.powered = powered;
    }

    // getters, setters, etc.
}
----

Smartphone detected in WiFi domain model:

[source,java]
----
package org.drools.quarkus.quickstart.test.model;

public class Smartphone {
    private final String name;

    public Smartphone(String name) {
        this.name = name;
    }

    // getters, setters, etc.
}
----

Alert class to hold information of the potential detected problems:

[source,java]
----
package org.drools.quarkus.quickstart.test.model;

public class Alert {
    private final String notification;

    public Alert(String notification) {
        this.notification = notification;
    }

    // getters, setters, etc.
}
----

Next, we create a rule file `rules.drl` inside the `src/main/resources/org/drools/quarkus/quickstart/test` folder of the Quarkus project.

[source,drl]
----
package org.drools.quarkus.quickstart.test;

unit HomeRuleUnitData;

import org.drools.quarkus.quickstart.test.model.*;

rule "No lights on while outside"
when
    $l: /lights[ powered == true ];
    not( /smartphones );
then
    alerts.add(new Alert("You might have forgot one light powered on: " + $l.getName()));
end

query "AllAlerts"
	$a: /alerts;
end

rule "No camera when present at home"
when
    accumulate( $s: /smartphones ; $count : count($s) ; $count >= 1 );
    $l: /cctvs[ powered == true ];
then
    alerts.add(new Alert("One CCTV is still operating: " + $l.getName()));
end
----

In this file there are some example rules to decide whether the overall status of the house is deemed inappropriate, triggering the necessary `Alert` (s).

Rule Unit a central paradigm introduced in Drools 8 that helps users to encapsulate the set of rules and the facts against which those rules will be matched; you can read more information in the https://www.drools.org/learn/documentation.html[Drools documentation].

The facts will be inserted into a `DataStore`, a type-safe entry point. To make everything work, we need to define both the RuleUnit and the DataStore.

[source,java]
----
package org.drools.quarkus.quickstart.test;

import org.drools.quarkus.quickstart.test.model.Alert;
import org.drools.quarkus.quickstart.test.model.CCTV;
import org.drools.quarkus.quickstart.test.model.Light;
import org.drools.quarkus.quickstart.test.model.Smartphone;
import org.drools.ruleunits.api.DataSource;
import org.drools.ruleunits.api.DataStore;
import org.drools.ruleunits.api.RuleUnitData;

public class HomeRuleUnitData implements RuleUnitData {
    
    private final DataStore<Light> lights;
    private final DataStore<CCTV> cctvs;
    private final DataStore<Smartphone> smartphones;

    private final DataStore<Alert> alerts = DataSource.createStore();

    public HomeRuleUnitData() {
        this(DataSource.createStore(), DataSource.createStore(), DataSource.createStore());
    }

    public HomeRuleUnitData(DataStore<Light> lights, DataStore<CCTV> cctvs, DataStore<Smartphone> smartphones) {
		this.lights = lights;
		this.cctvs = cctvs;
		this.smartphones = smartphones;
	}

	public DataStore<Light> getLights() {
		return lights;
	}

	public DataStore<CCTV> getCctvs() {
		return cctvs;
	}

	public DataStore<Smartphone> getSmartphones() {
		return smartphones;
	}

	public DataStore<Alert> getAlerts() {
		return alerts;
	}
}
----

== Testing the Application

We can create a standard Quarkus and JUnit test to check the behaviour of the Rule Unit and the defined rules, accordingly to a certain set of scenarios.

[source,java]
----
package org.drools.quarkus.quickstart.test;

@QuarkusTest
public class RuntimeIT {

    @Inject
    RuleUnit<HomeRuleUnitData> ruleUnit;

    @Test
    public void testRuleOutside() {
        HomeRuleUnitData homeUnitData = new HomeRuleUnitData();
        homeUnitData.getLights().add(new Light("living room", true));
        homeUnitData.getLights().add(new Light("bedroom", false));
        homeUnitData.getLights().add(new Light("bathroom", false));

        RuleUnitInstance<HomeRuleUnitData> unitInstance = ruleUnit.createInstance(homeUnitData);
        List<Map<String, Object>> queryResults = unitInstance.executeQuery("AllAlerts");
        assertThat(queryResults).isNotEmpty().anyMatch(kv -> kv.containsValue(new Alert("You might have forgot one light powered on: living room")));
    }
    
    @Test
    public void testRuleInside() {
        HomeRuleUnitData homeUnitData = new HomeRuleUnitData();
        homeUnitData.getLights().add(new Light("living room", true));
        homeUnitData.getLights().add(new Light("bedroom", false));
        homeUnitData.getLights().add(new Light("bathroom", false));
        homeUnitData.getCctvs().add(new CCTV("security camera 1", false));
        homeUnitData.getCctvs().add(new CCTV("security camera 2", true));
        homeUnitData.getSmartphones().add(new Smartphone("John Doe's phone"));

        RuleUnitInstance<HomeRuleUnitData> unitInstance = ruleUnit.createInstance(homeUnitData);
        List<Map<String, Object>> queryResults = unitInstance.executeQuery("AllAlerts");
        assertThat(queryResults).isNotEmpty().anyMatch(kv -> kv.containsValue(new Alert("One CCTV is still operating: security camera 2")));
    }
    
    @Test
    public void testNoAlerts() {
        HomeRuleUnitData homeUnitData = new HomeRuleUnitData();
        homeUnitData.getLights().add(new Light("living room", false));
        homeUnitData.getLights().add(new Light("bedroom", false));
        homeUnitData.getLights().add(new Light("bathroom", false));
        homeUnitData.getCctvs().add(new CCTV("security camera 1", true));
        homeUnitData.getCctvs().add(new CCTV("security camera 2", true));

        RuleUnitInstance<HomeRuleUnitData> unitInstance = ruleUnit.createInstance(homeUnitData);
        List<Map<String, Object>> queryResults = unitInstance.executeQuery("AllAlerts");
        assertThat(queryResults).isEmpty();
    }
}
----

== Wiring the Rule Unit with Quarkus CDI beans

We can now wire the Rule Unit into a standard Quarkus CDI bean, for general use in the Quarkus application.

For example, this might later be helpful to wire device status reporting through MQTT via Kafka, using the appropriate Quarkus extensions.

We create a simple CDI bean to abstract away the Rule Unit API usage with:

[source,java]
----
package org.drools.quarkus.quickstart.test;

@ApplicationScoped
public class HomeAlertsBean {
	
    @Inject
    RuleUnit<HomeRuleUnitData> ruleUnit;
    
    public Collection<Alert> computeAlerts(Collection<Light> lights, Collection<CCTV> cameras, Collection<Smartphone> phones) {
        HomeRuleUnitData homeUnitData = new HomeRuleUnitData();
        lights.forEach(homeUnitData.getLights()::add);
        cameras.forEach(homeUnitData.getCctvs()::add);
        phones.forEach(homeUnitData.getSmartphones()::add);

        RuleUnitInstance<HomeRuleUnitData> unitInstance = ruleUnit.createInstance(homeUnitData);
        var queryResults = unitInstance.executeQuery("AllAlerts");
        List<Alert> results = queryResults.stream()
                .flatMap(m -> m.values().stream()
                        .filter(Alert.class::isInstance)
                        .map(Alert.class::cast))
                .collect(Collectors.toList());
        return results;
    }
}
----

The same test scenarios can be refactored using this CDI bean accordingly.

[source,java]
----
package org.drools.quarkus.quickstart.test;

@QuarkusTest
public class BeanTest {

    @Inject
    HomeAlertsBean alerts;

    @Test
    public void testRuleOutside() {
        Collection<Alert> computeAlerts = alerts.computeAlerts(
                List.of(new Light("living room", true), new Light("bedroom", false), new Light("bathroom", false)),
                Collections.emptyList(),
                Collections.emptyList());

        assertThat(computeAlerts).isNotEmpty().contains(new Alert("You might have forgot one light powered on: living room"));
    }
    
    @Test
    public void testRuleInside() {
        Collection<Alert> computeAlerts = alerts.computeAlerts(
                List.of(new Light("living room", true), new Light("bedroom", false), new Light("bathroom", false)),
                List.of(new CCTV("security camera 1", false), new CCTV("security camera 2", true)),
                List.of(new Smartphone("John Doe's phone")));
        
        assertThat(computeAlerts).isNotEmpty().contains(new Alert("One CCTV is still operating: security camera 2"));
    }
    
    @Test
    public void testNoAlerts() {
        Collection<Alert> computeAlerts = alerts.computeAlerts(
                List.of(new Light("living room", false), new Light("bedroom", false), new Light("bathroom", false)),
                List.of(new CCTV("security camera 1", true), new CCTV("security camera 2", true)),
                Collections.emptyList());
        
        assertThat(computeAlerts).isEmpty();
    }
}
----

== Running the Application

To run the Quarkus project, you can reference the link:{quarkus-guides-url}[Quarkus Guide].

== Where to go from here

This was a minimal example using DRL, Rule Unit and the Drools Quarkus extension; as you can see, this is a very unopinionated and straightforward way to use the Drools rule engine inside a Quarkus application, in order to finally create a fully functioning _intelligent_ microservice on top of Quarkus!

To see additional capabilities of Drools, you can reference the documentation at the links below.

For a more opinionated platform including integrating with DMN (Drools DMN engine), Processes (CNCF's Serveless Workflow, BPMN2), Rules (Drools' DRL), Prediction (PMML), Test Scenario (visual notation for testing), assisted deployment to OpenShift, and many more capabilities, you can checkout the Kogito platform. Kogito is a next generation intelligent automation toolkit that originates from well known Open Source projects such as Drools (for business rules) and processes.

== References

* https://drools.org[Drools website]
* https://www.drools.org/learn/documentation.html[Drools documentation]
* https://kogito.kie.org[Kogito Website]
* https://docs.jboss.org/kogito/release/latest/html_single[Kogito Documentation]
