myherux-drools-springboot

repository·master·Indexed 20 days ago

https://github.com/myherux/drools-springboot

A project demonstrating the integration of the Drools rule engine (version 6.5.0.Final) with the Spring Boot framework. It provides guides on DRL syntax, rule attributes, and managing fact lifecycles using insert() and update(). The documentation includes examples for solving complex business logic, state-based problems (MingDrink), and constraint satisfaction problems (GolferProblem), as well as configuration via kmodule.xml.

Tokens
5.6K
Snippets
12
Records
18
Agent score
73%

What's inside myherux-drools-springboot

  1. Write Rule Conditions (LHS)

    master

    The Left Hand Side (LHS) defines the patterns that must match for a rule to fire.

    Pattern Matching Types

    • No constraints: Person() (matches any Person fact).
    • Field constraints: Person( name == "bob" ).
    • Field binding: Person( $name : name == "bob" ) (binds the value of name to variable $name).
    • Fact binding: $bob : Person( name == "bob" ) (binds the entire object to $bob).
    • Variable constraints: Person( name == $name ).

    Comparison Operators

    Drools supports: >, >=, <, <=, ==, !=, contains, not contains, memberOf, not memberOf, matches, not matches.

    • contains: Checks if a collection or string contains a value.
    • memberOf: Checks if a field is a member of a specific variable (collection).
    • matches: Performs regex matching (does not require escaping / like Java).
    • exists: Activates the rule if at least one matching fact exists (activates at most once).
    • not: Activates if no matching facts exist.
  2. Use insert() and update() to manage fact lifecycles in Drools

    master

    When solving problems that require discovering new information or updating existing state, use these core Drools actions:

    • insert(new Fact()): Adds a new fact into the working memory during rule execution. This is useful when one rule's conclusion provides a new piece of data that other rules need to process.
    • update(fact): Notifies the engine that a fact has changed. This triggers the engine to re-evaluate rules that depend on that fact's properties.
    • salience: A keyword used to assign priority to rules. Higher salience values are executed first.
  3. Drools Language Keywords and Comments

    master

    Drools uses specific keywords for rule definition.

    Keywords

    • Hard Keywords (Reserved, cannot be used as identifiers): true, false, null.
    • Soft Keywords (Avoid using as identifiers to prevent conflicts): lock-on-active, date-effective, date-expires, no-loop, auto-focus, activation-group, agenda-group, ruleflow-group, entry-point, duration, package, import, dialect, salience, enabled, attributes, rule, extend, when, then, template, query, declare, function, global, eval, not, in, or, and, exists, forall, accumulate, collect, from, action, reverse, result, end, over, init.

    Comments

    • Single-line comments: Use //.
    • Multi-line comments: Use /* ... */.
  4. Explore Drools rules and usage guides

    master

    The repository provides several guides for working with Drools:

    • Drools Language: Learn the syntax and language specifications.
    • Rule Examples: View concrete implementations of rules.
    • Dynamic Rules and Decision Tables: Learn how to use dynamic rules and decision tables within Drools.
    • Solving Complex Logic: Guidance on using Drools to handle complex business logic problems.
  5. Convert Excel Decision Tables to DRL strings

    master

    You can use the SpreadsheetCompiler to translate Excel-based decision tables into standard DRL rule language. This allows you to manage rules in a user-friendly spreadsheet format and convert them into executable code at runtime.

    There are two ways to perform this conversion:

    1. From a Classpath Resource: Use ResourceFactory.newClassPathResource if the Excel file is bundled in your application.
    2. From an InputStream: Use ResourceFactory.newInputStreamResource if the Excel file is provided via a file upload or external stream.
    // Option 1: Compile from a classpath Excel file
    public String getRuleTable() {
        SpreadsheetCompiler compiler = new SpreadsheetCompiler();
        String rules = compiler.compile(ResourceFactory.newClassPathResource(RULES_PATH + File.separator + "rule.xlsx", "UTF-8"), "rule-table");
        return rules;
    }
    
    // Option 2: Compile from an InputStream (e.g., uploaded file)
    public String getRuleTable(InputStream inputStream) {
        SpreadsheetCompiler compiler = new SpreadsheetCompiler();
        String rules = compiler.compile(ResourceFactory.newInputStreamResource(inputStream, "UTF-8"), "rule-table");
        return rules;
    }
  6. Define Drools rules (DRL syntax)

    master

    Drools rules consist of three main parts: the header (package and imports), the condition (LHS - Left Hand Side), and the action (RHS - Right Hand Side).

    Key components used in the example:

    • salience: Defines rule priority (higher numbers execute first).
    • exists( Type() ): A condition that checks if an object of a specific type exists in working memory.
    • not( Type() ): A condition that checks if an object of a specific type does NOT exist.
    • insertLogical( new Type() ): Inserts a new object into the working memory logically (it will be removed if the condition that triggered it is no longer met).
    • modify( object ) { ... }: Updates an existing object in working memory, triggering re-evaluation of rules.
    • package: Specifies the namespace for the rules.
    • import: Imports the Java bean classes used in the rules.
    package com.xu.drools
    
    import com.xu.drools.bean.Politician;
    import com.xu.drools.bean.Hope;
    
    rule "We have an honest Politician"
        salience 10
        when
            exists( Politician( honest == true ) )
        then
            insertLogical( new Hope() );
    end
    
    rule "Corrupt the Honest"
        when
            politician : Politician( honest == true )
            exists( Hope() )
        then
            System.out.println( "I'm an evil corporation and I have corrupted " + politician.getName() );
            modify( politician ) {
                setHonest( false )
            }
    end
  7. Dynamically generate a KieSession from DRL strings

    master

    You can create a new KieSession at runtime by passing a raw DRL (Drools Rule Language) string. This involves writing the string to a virtual KieFileSystem, building the KieBuilder, and extracting a KieBase.

    Note: If the DRL string contains syntax errors, the builder will return error messages. You should check results.hasMessages(Message.Level.ERROR) to handle compilation failures.

    public KieSession getKieSession(String rules) {
        KieServices kieServices = KieServices.Factory.get();
        KieFileSystem kfs = kieServices.newKieFileSystem();
        // Write the dynamic rules string to the virtual file system
        kfs.write("src/main/resources/rules/rules.drl", rules.getBytes());
        
        KieBuilder kieBuilder = kieServices.newKieBuilder(kfs).buildAll();
        Results results = kieBuilder.getResults();
        
        if (results.hasMessages(org.kie.api.builder.Message.Level.ERROR)) {
            // Handle compilation errors
            throw new BusinessException(300003, results.getMessages().toString(), 4);
        }
        
        KieContainer kieContainer = kieServices.newKieContainer(kieServices.getRepository().getDefaultReleaseId());
        KieBase kieBase = kieContainer.getKieBase();
    
        return kieBase.newKieSession();
    }
  8. Define Packages, Imports, and Globals in Drools

    master

    Rules files require specific structural elements at the top level.

    • package: Defines a namespace. It is mandatory and must be the first line of the rule file.
    • import: Works like Java imports. You must specify the fully qualified path and type name for any object used in rules.
    • global: Defines global variables accessible within rules.

    To use a global, declare it in the rule file and set its value in the Java code using kieSession.setGlobal("name", value).

    // In the .drl file
    global java.util.List myGlobalList;
    
    rule "Using a global"
    when
      eval( true )
    then
      myGlobalList.add( "Hello World" );
    end
    
    // In the Java code
    List list = new ArrayList();
    KieSession kieSession = kiebase.newKieSession();
    kieSession.setGlobal( "myGlobalList", list );
  9. Configure Drools using kmodule.xml

    master

    To use the kmodule approach for calling Drools, you must create a configuration file at /resources/META-INF/kmodule.xml. This file defines the kbase (Knowledge Base) and the ksession (Knowledge Session) used to execute rules.

    In the example, the kbase is named HonestPoliticianKB and points to the package com.xu.drools.rule.honestpolitician, which contains the rule definitions.

    <?xml version="1.0" encoding="UTF-8"?>
    <kmodule xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xmlns="http://www.drools.org/xsd/kmodule">
        <kbase name="HonestPoliticianKB" packages="com.xu.drools.rule.honestpolitician">
            <ksession name="HonestPoliticianKS"/>
        </kbase>
    </kmodule>
  10. Add Drools dependencies to a Spring Boot project

    master

    To use the Drools rule engine within a Spring Boot application, include the following Maven dependencies. This project uses Drools version 6.5.0.Final.

    <properties>
        <drools.version>6.5.0.Final</drools.version>
    </properties>
    
    <!--Drools-->
    <dependency>
        <groupId>org.kie</groupId>
        <artifactId>kie-api</artifactId>
        <version>${drools.version}</version>
    </dependency>
    <dependency>
        <groupId>org.drools</groupId>
        <artifactId>drools-core</artifactId>
        <version>${drools.version}</version>
    </dependency>
    <dependency>
        <groupId>org.drools</groupId>
        <artifactId>drools-compiler</artifactId>
        <version>${drools.version}</version>
    </dependency>
    <dependency>
        <groupId>org.drools</groupId>
        <artifactId>drools-decisiontables</artifactId>
        <version>${drools.version}</version>
    </dependency>
    <dependency>
        <groupId>org.drools</groupId>
        <artifactId>drools-templates</artifactId>
        <version>${drools.version}</version>
    </dependency>
  11. Solve constraint satisfaction problems with Drools (GolferProblem example)

    master

    Drools can be used to solve complex constraint satisfaction problems (like logic puzzles) by defining multiple facts and using the when clause to enforce constraints across them.

    You can use pattern matching to ensure that different objects (e.g., different golfers) do not share the same attributes (like position or color) by using inequality operators (!=) and comparing properties of bound variables.

    rule "find solution"
        when
            // Define constraints for multiple objects
            $fred : Golfer( name == "Fred" )
    
            $joe : Golfer( name == "Joe",
                    position == 2,
                    position != $fred.position,
                    color != $fred.color )
    
            $bob : Golfer( name == "Bob",
                    position != $fred.position,
                    position != $joe.position,
                    color == "plaid",
                    color != $fred.color,
                    color != $joe.color )
    
            // Constraint: Fred's neighbor to the right wears blue
            Golfer( position == ( $fred.position + 1 ),
                    color == "blue",
                    this in ( $joe, $bob, $tom ) )
        then
            System.out.println( "Fred " + $fred.getPosition() + " " + $fred.getColor() );
            // ... print other results
    end
  12. Execute dynamic rules with a KieSession

    master

    To run rules against data, follow these steps:

    1. Obtain a KieSession using your dynamic rule provider.
    2. Deserialize your input data (e.g., from JSON) into a Fact object.
    3. insert the Fact object into the session.
    4. Call fireAllRules() to trigger the rule engine.
    5. Crucial: Call dispose() on the session to release resources and prevent memory leaks.
    KieSession kieSession = rulesService.getKieSession(rule);
    Gson gson = new Gson();
    Person person = gson.fromJson(json, Person.class);
    
    // Insert the fact into the engine
    kieSession.insert(person);
    
    // Execute rules
    kieSession.fireAllRules();
    
    // Clean up resources
    kieSession.dispose();