DroolsStatefulKnowledgeSession und Cocktails

Beach of Florianopolis, by Christoph Burmeister (own photo)

Beach of Florianopolis, by Christoph Burmeister (own photo)


Ich hab mal wieder ein bisschen mit Drools rumgespielt und verzweifelt nach einem Beispiel gesucht, wie man das nicht nur für Enterprise-Applications nutzen kann. Was liegt da näher als die Saft- und Alkohol-Reste zu verwenden? 🙂

Stellen wir uns vor, wir hätten Unmengen von Resten, alle fein säuberlich in Centilitern geteilt. Das kippen wir jetzt alles in einen Eimer, oder in ein Working-Memory von Drools.

zuerst die Zutaten-Klassen, alle von einer gemeinsamen Liquid-Superklasse abgeleitet:

package org.jtaddeus.cocktailapp.ingredients;

public class CherryJuice extends Liquid {
	public CherryJuice(int cl) {
		this.cl = cl;
	}
}

und die Superklasse:

package org.jtaddeus.cocktailapp.ingredients;

public class Liquid {

	protected int cl;

	public int getCl() {
		return cl;
	}

	public void setCl(int cl) {
		this.cl = cl;
	}
	
}

die fertigen Cocktail-Klassen TeqilaSunrise und SexOnTheBeach:

package org.jtaddeus.cocktailapp.cocktails;

public class TequilaSunrise {

}

die Drools-Regeln:

package com.tsystems.ass2nms.alarming.logic.enricher

import org.jtaddeus.cocktailapp.ingredients.Grenadine
import org.jtaddeus.cocktailapp.ingredients.LemonJuice
import org.jtaddeus.cocktailapp.ingredients.OrangeJuice
import org.jtaddeus.cocktailapp.ingredients.PineAppleJuice
import org.jtaddeus.cocktailapp.ingredients.CherryJuice
import org.jtaddeus.cocktailapp.ingredients.Tequila
import org.jtaddeus.cocktailapp.ingredients.Wodka
import org.jtaddeus.cocktailapp.ingredients.PeachLiqueur
import org.jtaddeus.cocktailapp.cocktails.SexOnTheBeach
import org.jtaddeus.cocktailapp.cocktails.TequilaSunrise

query "count TequilaSunrise"	
	tequilaSunrise : TequilaSunrise()
end

rule "Tequila Sunrise"
    activation-group "cocktails"
    salience 50
    when
    	grenadine : Grenadine(
    					cl >= 2, 
    					amountGrenadine : cl
    				)
    	lemonJuice : LemonJuice(
    					cl >= 1, 
    					amountLemonJuice : cl
    				)
    	orangeJuice : OrangeJuice(
    					cl >= 12, 
    					amountOrangeJuice : cl
    				)
    	tequila : Tequila(
    					cl >= 5, 
    					amountTequila : cl
    				)          
    then
        System.out.println("RULE FIRED:" + drools.getRule().getName());  
        
        String ingredients = "   Tequila:6cl, OrangeJuice:12cl, LemonJuice:1cl, Grenadine:2cl";        	
        System.out.println(ingredients);
        insert(new TequilaSunrise());
                
        grenadine.setCl(amountGrenadine-2);        
        lemonJuice.setCl(amountLemonJuice-1);        
        orangeJuice.setCl(amountOrangeJuice-12);        
        tequila.setCl(amountTequila-5);
        
        update(grenadine);
        update(lemonJuice);
        update(orangeJuice);
        update(tequila);               
end

rule "Sex on the Beach"
    activation-group "cocktails"
    salience 50
    when
    	peachLiqueur : PeachLiqueur(
    					cl == 3, 
    					amountPeachLiqueur : cl
    				)
    	wodka : Wodka(
    					cl == 4, 
    					amountWodka : cl
    				)
    	orangeJuice : OrangeJuice(
    					cl == 6, 
    					amountOrangeJuice : cl
    				)
    	pineAppleJuice : PineAppleJuice(
    					cl == 6, 
    					amountPineAppleJuice : cl
    				)
    	cherryJuice : CherryJuice(
    					cl == 4, 
    					amountCherryJuice : cl
    				)       
    then
        System.out.println("RULE FIRED:" + drools.getRule().getName());  
        String ingredients = "   PeachLiqueur: " + amountPeachLiqueur + ", Wodka: " + amountWodka + ", OrangeJuice: " + amountOrangeJuice + ", PineAppleJuice " + amountPineAppleJuice + ", CherryJuice " + amountCherryJuice;
        System.out.println(ingredients);
        insert(new SexOnTheBeach());
       
end

Und der Unit-Test, der die Session aufbaut, die Facts (vorhandene Zutaten) reinwirft und die Anzahl der TequilaSunrise-Objekte über einen Query zählt.

package org.jtaddeus;

import static org.junit.Assert.assertEquals;

import org.drools.KnowledgeBase;
import org.drools.KnowledgeBaseFactory;
import org.drools.builder.KnowledgeBuilder;
import org.drools.builder.KnowledgeBuilderFactory;
import org.drools.builder.ResourceType;
import org.drools.io.ResourceFactory;
import org.drools.runtime.StatefulKnowledgeSession;
import org.drools.runtime.rule.QueryResults;
import org.jtaddeus.cocktailapp.ingredients.Grenadine;
import org.jtaddeus.cocktailapp.ingredients.LemonJuice;
import org.jtaddeus.cocktailapp.ingredients.OrangeJuice;
import org.jtaddeus.cocktailapp.ingredients.Tequila;
import org.junit.Test;

public class ProcessorTest {
	private StatefulKnowledgeSession createStatefulSession() {

        KnowledgeBuilder kbuilder = KnowledgeBuilderFactory.newKnowledgeBuilder();
        kbuilder.add(ResourceFactory.newClassPathResource("drools/processor.drl"), ResourceType.DRL);
        if (kbuilder.hasErrors()) {
            System.out.println(kbuilder.getErrors());
        }

        KnowledgeBase kbase = KnowledgeBaseFactory.newKnowledgeBase();
        kbase.addKnowledgePackages(kbuilder.getKnowledgePackages());

        StatefulKnowledgeSession ksession = kbase.newStatefulKnowledgeSession();
        System.out.println("---created ksession");
        return ksession;
        
    }
	
	@Test
	public void testTequilaSunrise(){
		int n = 5;
		StatefulKnowledgeSession ksession = createStatefulSession();
		ksession.insert(new Grenadine(2*n)); // 2 for 1
		ksession.insert(new LemonJuice(1*n)); // 1 for 1
		ksession.insert(new OrangeJuice(12*n)); // 12 for 1
		ksession.insert(new Tequila(5*n)); // 5 for 1
		ksession.fireAllRules();
		ksession.dispose();
		System.out.println("---disposed ksession");

        QueryResults queryResults = ksession.getQueryResults("count TequilaSunrise");
        assertEquals(n, queryResults.size()); // there must be n facts of type TequilaSunrise in WorkingMemory
	}
	
//	@Test
//	public void testSexOnTheBeach(){
//		StatefulKnowledgeSession ksession = createStatefulSession();
//		ksession.insert(new PeachLiqueur(3));
//		ksession.insert(new Wodka(4));
//		ksession.insert(new OrangeJuice(6));
//		ksession.insert(new PineAppleJuice(6));
//		ksession.insert(new CherryJuice(4));
//		ksession.fireAllRules();
//		ksession.dispose();
//		System.out.println("---disposed ksession");
//	}
}

jetzt noch die Maven-3-pom und schick:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>org.jtaddeus</groupId>
	<artifactId>droolstest</artifactId>
	<version>0.0.1-SNAPSHOT</version>

	<dependencies>
		<dependency>
			<groupId>org.drools</groupId>
			<artifactId>drools-core</artifactId>
			<version>5.3.0.Final</version>
		</dependency>
                <dependency>
                        <groupId>org.drools</groupId>
                        <artifactId>drools-compiler</artifactId>
                        <version>5.3.0.Final</version>
                        <type>jar</type>
                </dependency>
		<dependency>
			<groupId>junit</groupId>
			<artifactId>junit</artifactId>
			<version>4.8.2</version>
		</dependency>

	</dependencies>
</project>

und die Projekt-Struktur: