Accueil > Divers > Démarrer avec Drools et autres astuces

Démarrer avec Drools et autres astuces

Pour le projet de recherche Synodos (http://www.viseo.com/fr/offre/le-projet-synodos), nous avons choisi Drools comme moteur de règles, un framework simple, souple et puissant. C’est pourquoi j’ai décidé de partager mon expérience à travers une suite de trois articles: le premier présentant Drools et comment débuter avec, et les 2 autres présentant quelques astuces dont j’ai eu besoin au cours de mon projet :

Article n°1 : Démarrer avec Drools (celui-ci donc)

Article n°2 : Mise à jour de faits et boucles infinies (http://blog.viseo-bt.com/drools-mise-a-jour-de-faits-et-boucles-infinies/)

Article n°3 : Manipulation de l’ordre de déclenchement des règles (http://blog.viseo-bt.com/drools-manipulation-de-lordre-de-declenchement-des-regles/)

 

Article n°1 : Démarrer avec Drools

 

 

1- Drools, c’est quoi ?

 

Drools est ce qu’on appelle un Business Rules Management System (BRMS) ou en bon français un Système de Gestion de Règles métier. Bref, ça va nous aider à gérer les règles métier.

Les règles métier, on en trouve partout: dans les banques, les assurances, les industries, les sociétés de commerce, l’administration …, et s’expriment dans la plus simple de leur forme par:

SI x ALORS y.

x est la condition, y est la conclusion.

 

Ex de règle métier qu’on pourrait trouver dans un banque:

SI la personne est client depuis 5 ans ALORS lui proposer un meilleur taux.

 

Si nous devions coder ça, nous pourrions utiliser le simple opérateur conditionnel IF.

Mais si la règle se mettait à changer? Finalement, c’est quand la personne est client depuis 3 ans qu’on lui propose un meilleur taux. Ca va peut-être attirer plus de clients ? … Mais si la personne est client depuis seulement un an mais qu’il a 2 comptes, ça vaut peut-être le coup de lui proposer un meilleur taux aussi…

Les règles métier, comme elles s’adaptent au … métier, peuvent évoluer tout le temps et ça va vite être la pagaille avec des IF s’il faut modifier le code à chaque fois.

La solution ? Découpler les règles métier du code applicatif à l’aide d’un BRMS, comme Drools.

Comment ça fonctionne ?

 

Drools est à la base un système expert mais doté de nombreuses fonctionnalités pour faciliter son utilisation.

Reprenons alors l’exemple de la banque pour rappeler ce qu’est un système expert:

Nous avons une liste de personnes et pour chaque personne, il faut définir quel taux lui attribuer, à l’aide d’un ensemble de règles.

Chaque personne a ses propres caractéristiques: depuis quand elle est cliente, le nombre de comptes qu’elle a, la valeur moyenne de son/ses compte(s)… Cela va constituer les « faits ».

Le système expert va se servir de toutes ces connaissances (faits et règles) pour faire des inférences et en déduire de nouveaux faits (ou conclusions), ici, le taux attribué à chaque personne. Il va faire un raisonnement. Pour cela, il est composé d’un moteur d’inférence qui va déterminer l’ensemble des règles applicables à un fait. On parle d’évaluation de règles, et cet ensemble de règles applicables constitue ce qu’on appelle l’agenda. Il va ensuite exécuter ces règles, on parle de déclenchement de règles.

schema_systeme_expert

Avec Drools, les faits et les règles doivent être chargés dans la « mémoire de travail » pour pouvoir être pris en compte par le moteur d’inférence.

Dans le projet Synodos (http://www.viseo.com/fr/offre/le-projet-synodos), les faits étaient tous les symptômes, maladies, vaccins, … enregistrés dans le dossier électronique d’un patient. Les règles étaient des règles médicales écrites par les médecins eux-mêmes. Nous avions construit une interface Web leur permettant de saisir facilement ces règles, que nous traduisons en « langage Drools » ensuite. Le but était de déterminer automatiquement si un patient était susceptible de développer une certaine maladie ou pas.

Voilà pour la théorie, passons à la pratique.

 

2-    Hello World avec Eclipse JEE Luna

 

La dernière release stable de Drools à l’écriture de cet article est la version 6.1.

 

Installation du plug-in Drools pour Eclipse:

 

Entrez l’URL http://download.jboss.org/drools/release/6.1.0.Final/org.drools.updatesite/ dans Help > Install new software.

Pour notre exemple, seuls les composants Drools Core et Drools Detector sont nécessaires. On peut voir que le plug-in Drools est bien installé après le redémarrage d’Eclipse en vérifiant qu’il existe une perspective Drools dans les perspectives disponibles.

 

Installation de l’environnement d’exécution (runtime) Drools:

 

– Téléchargez la version 6.1 de Drools à l’adresse http://www.drools.org/download/download.html (environ 90 Mo)

– Dézippez l’archive dans un répertoire (ici, je l’ai nommé drools-distribution-6.1.0.Final) et indiquez-le à Eclipse dans Window > Preferences > Drools pour créer un nouvel environnement d’exécution Drools.

Nouvel_environnement_Drools

– Une fois ajouté, sélectionnez le runtime et appliquez les changements.

 

Test de l’environnement avec un nouveau projet Drools

 

– Créez un nouveau projet Drools dans New > Other > Drools > Drools Project .

Le runtime précédemment choisi sera sélectionné par défaut.

– Exécutez le fichier DroolsTest situé dans com.sample. Si vous voyez dans la console:

Hello World

Goodbye cruel world

C’est que notre installation marche.

Passons maintenant à notre propre exemple à nous qui va nous aider à comprendre comment on fait.

 

3- Projet simple étape par étape:

 

Notre petite application est utilisée dans une banque pour savoir si une personne peut avoir droit ou non à un prêt. Pour cela, chaque information de son dossier (dite « Caractéristique ») peut apporter des points positifs (pros) ou négatifs (cons) à son dossier. Si la personne a droit à un prêt, elle est dite « solvable ».

 

Création des faits

 

Voici la classe Personne:

package com.pojo;
public class Personne {
	private int numDossier;
	private int pros;
	private int cons;
	private boolean solvable;
public Personne(){}
public Personne(int numDossier) {
		super();
		this.numDossier = numDossier;
	}
//getters and setters …
}

Et la classe Caracteristique :

package com.pojo;
public class Caracteristique {
	private int numDossier;
	private String nom;
	private int valeur;
	
	public Caracteristique(){}
	public Caracteristique(int numDossier, String nom, int valeur) {
		super();
		this.numDossier = numDossier;
		this.nom = nom;
		this.valeur = valeur;
//getters and setters …
	}
}

Ces personnes et ces caractéristiques, une fois instanciées, constituent donc nos faits. Pour simplifier, nous avons deux listes d’objets qui vont être nos faits:

//Initialisation des données
		List<Personne> personnes=new ArrayList<Personne>();
		List<Caracteristique> caracteristiques=new ArrayList<Caracteristique>();

		Personne p1=new Personne(1);
		personnes.add(p1);

		Caracteristique c1=new Caracteristique(1, "CDI", 1);
		Caracteristique c2=new Caracteristique(1, "Salaire", 48345);
		Caracteristique c3=new Caracteristique(1, "NombreEnfants", 3);
		caracteristiques.add(c1);
		caracteristiques.add(c2);
		caracteristiques.add(c3);

Mettez cette initialisation dans la méthode main.

Idéalement, ces faits sont extraits d’une structure de données (base de données, fichiers csv…).

 

Création d’une règle

 

Soit la règle:

SI la personne a un CDI ALORS elle a un point positif de plus dans son dossier.

Dans un projet Drools, les règles sont écrites dans un fichier DRL placés par défaut dans src/main/resources/rules, dans le langage MVEL et un peu de Java. Rassurez-vous, aucun pré-requis en MVEL n’est nécessaire pour débuter en Drools.

Supprimez le fichier Sample.drl et créez un nouveau fichier EvaluationDossier.drl.

Pour cette 1ère règle, on aura dans ce fichier:

package com.sample
 
import com.pojo.*;
rule "a un CDI"
    when
        personne: Personne( $numDossierPersonne:numDossier  )
        c: Caracteristique( numDossier == $numDossierPersonne, nom == 'CDI' , valeur ==1  )
    then
        personne.setPros(personne.getPros()+1);
        System.out.println("Règle \"a un CDI\" déclenchée !");
end

Expliquons ligne par ligne ce fichier:

 

L’en-tête du fichier:

  • package com.sample

=> comme un fichier java, chaque fichier DRL doit appartenir à un package. Nous avons laissé celui par défaut.

  • import com.pojo.*

=> Nous devons importer les classes que nous allons utiliser dans les règles.

 

La définition de la règle:

  •   rule« a un CDI »

=> Toute règle commence par le mot-clé rule, suivi du nom de la règle qui doit être unique.

  • Une règle est composée de deux parties.

=> La condition, introduite par when. Dans Drools, on parle de Left-Hand Side (LHS)

=> La conclusion, introduite par then. C’est le Right-Hand Side (RHS)

  • Sa fin est obligatoirement marquée par le mot end.

 

La condition de la règle:

  • Elle est essentiellement écrite en MVEL et peut être constituée de une ou plusieurs sous-conditions
  • La syntaxe la plus simple d’une sous-condition est la suivante:

=> instance_objet_x: ObjetX ()

Seule la présence d’une instance d’ObjetX est exigée pour que la règle soit déclenchée.

  • Ici, pour satisfaire la règle, 1ère sous-condition: il faut qu’il y ait une personne dans les faits:

=> personne: Personne( $numDossierPersonne:numDossier )

On sauvegarde son numéro de dossier dans la variable $numDossierPersonne (on met un dollar au début pour le différencier des variables Java, ce n’est pas obligatoire). Elle nous servira à faire le lien avec la caractéristique.

On remarquera l’opérateur ‘:‘ pour l’affectation de variable.

  • 2è sous-condition: Il faut qu’elle ait un CDI, soit une caractéristique de nom « CDI » avec une valeur égale à 1 :

=> c: Caracteristique( numDossier == $numDossierPersonne, nom == ‘CDI’ , valeur ==1 )

Pour séparer plusieurs termes de la sous-condition, on utilise la virgule qui a la valeur logique ET.

 

La conclusion de la règle:

  • Si l’ensemle de ces sous-conditions est satisfaite, la partie Conclusion est exécutée: incrémenter de 1 la valeur de pros d’une personne.
  • La partie Conclusion peut s’écrire entièrement en Java:

=> personne.setPros(personne.getPros()+1);

System.out.println(« Règle \ »a un CDI\ » déclenchée ! »);

  • Pour vérifier si la règle est utilisée, nous avons ajouté l’affichage d’un message dans la console.

Note:l’autocomplétion marche bien sur les fichiers DRL, mais il faut bien faire Ctrl + Espace pour afficher les suggestions.

 

Création d’une session Drools

 

Créez la classe ManageExpertSystem suivante:

public class ManageExpertSystem {

	private KieContainer kieContainer;

	public KieSession createKieSession() {
		KieServices kieServices = KieServices.Factory.get();
		KieResources kieResources = kieServices.getResources();
		KieFileSystem kieFileSystem = kieServices.newKieFileSystem();

		Resource resource=kieResources.newClassPathResource("EvaluationDossier.drl");
		String resourcepath = "src/main/resources/rules" + "/"
				+ "RulesCompiled.drl";
		kieFileSystem.write(resourcepath, resource);

		KieBuilder kb = kieServices.newKieBuilder(kieFileSystem);
		kb.buildAll();
		if (kb.getResults().hasMessages(Message.Level.ERROR)) {
			throw new RuntimeException("Build Errors:\n"
					+ kb.getResults().toString());
		}
		ReleaseId ridOriginal = kieServices.newReleaseId("org.default", "artifact",
				"1.0.0-SNAPSHOT");
		kieContainer = kieServices.newKieContainer(ridOriginal);
		return kieContainer.newKieSession();

	}
}

Note: Beaucoup de classes de Drools commencent par KIE qui signifie « Knowledge Is Everything ». Ça a été un grand changement introduit à partir de la version 6 de Drools.

Dans la méthode createKieSession, nous avons créé une session Drools dans laquelle seront insérés les faits et les règles et depuis laquelle le moteur d’inférence sera appelé.

– Pour cela, on a commencé par créer un service Drools.

– On ajoute ensuite le répertoire du fichier DRL au build path du projet pour pouvoir être rajouté comme ressource dans la session Drools.

– Les règles vont être « compilées » dans un fichier fictif (ici, RulesCompiled.drl) pour être chargées dans la mémoire de travail.

– A leur chargement, la validité de chacune des règles va être vérifiée par un « Builder ».

– On créé une version de la session pour contenir les règles chargées. La version définie ici est la version par défaut proposée par Drools.

– On va charger cette version et le service associé dans un conteneur et récupérer la session qui en résulte.

Note: Il existe plusieurs manières de créer une session Drools. Elle est ici plus compliquée pour pouvoir spécifier précisément quel fichier utiliser et attribuer une version à notre base de règles. Vous pouvez voir une manière plus simple dans l’exemple par défaut fourni dans un nouveau projet Drools. Pour plus de détails, allez faire un tour dans la javadoc: http://docs.jboss.org/jbpm/v6.1/javadocs/.

 

Chargement des connaissances

 

Une fois notre session créée, on va pouvoir l’utiliser pour charger nos faits. L’insertion de faits se fait de manière très simple, via la méthode insert().

Dans la classe ManageExpertSystem précédente, ajoutez la méthode suivante:

public void loadFacts(KieSession kSession, List<Personne> personnes, List<Caracteristique> caracteristiques){
		for(Personne p:personnes){
			kSession.insert(p);
		}

		for(Caracteristique c: caracteristiques){
			kSession.insert(c);
		}
	}

Lancement du moteur d’inférence

 

Dans notre méthode main, après avoir initialisé les données, ajoutez les lignes suivantes:

//initialisation du système expert
		ManageExpertSystem mes=new ManageExpertSystem();
		KieSession kSession=mes.createKieSession();
		mes.loadFacts(kSession, personnes, caracteristiques);

		//exécution du moteur d'inférence	
		kSession.fireAllRules();

		//Affichage des résultats
		for(Personne p: personnes){
			System.out.println(" Dossier n°"+ p.getNumDossier()+ ": ");
			System.out.println(" \t -Pros = " + p.getPros());
			System.out.println(" \t -Cons = " + p.getCons());
		}

Le moteur d’inférence va tourner avec l’appel de la méthode fireAllRules() et nous obtenons dans notre console:

Règle « a un CDI » déclenchée !

Dossier n°1:

-Pros = 1

-Cons = 0

Nous savons maintenant combien de points positifs et de points négatifs a le dossier de chaque personne.

Si nous voulons ajouter ou modifier des règles, ça ne se fera que dans le fichier DRL sans toucher au code Java.

Vous pouvez maintenant jouer avec tout ça en ajoutant plein de faits et de règles, ou même créer un nouveau scénario.

Voici un autre exemple de règle pour vous aider :

 

rule "a un bon salaire"
    when
      personne: Personne( $numDossierPersonne:numDossier  )
      c: Caracteristique( numDossier == $numDossierPersonne, nom == 'Salaire' , valeur >=45000, valeur <60000  )
    then
        personne.setPros(personne.getPros()+2);
        System.out.println("Règle \"a un bon salaire\" déclenchée !");
end 

Après avoir rajouté cette règle, nous devrions donc avoir le message suivant en console :

Règle « a un CDI » déclenchée !

Règle « a un bon salaire » déclenchée !

Dossier n°1:

-Pros = 3

-Cons = 0

 

Pour finir cet article, je vous conseille vivement la lecture du chapitre de la doc Drools sur l’écriture des règles pour utiliser pleinement les possibilités de Drools: http://docs.jboss.org/drools/release/6.1.0.Final/drools-docs/html_single/index.html#DroolsLanguageReferenceChapter.

Et pour d’autres astuces sur Drools, voici les liens de mes articles suivants :

– Mise à jour de faits et boucles infinies (http://blog.viseo-bt.com/drools-mise-a-jour-de-faits-et-boucles-infinies/)

– Manipulation de l’ordre de déclenchement des règles (http://blog.viseo-bt.com/drools-manipulation-de-lordre-de-declenchement-des-regles/)

Categories: Divers Tags:
  1. milly
    22/01/2016 à 16:41 | #1

    Bonjour, merci pour ce petit tuto, je suis toutes les étapes sauf qu’à l’exécution du DroolsTest.java j’ai le message suivant, pourriez vous me dire comment le résoudre?
    merci !

    298 [main] INFO org.kie.internal.utils.ServiceDiscovery – Discovered kie.conf url=jar:file:/D:/Desktop/drools-distribution-6.3.0.Final/drools-distribution-6.3.0.Final/binaries/drools-beliefs-6.3.0.Final.jar!/META-INF/kie.conf
    378 [main] INFO org.kie.internal.utils.ServiceDiscovery – Adding Assembler org.drools.beliefs.bayes.assembler.BayesAssemblerService
    379 [main] INFO org.kie.internal.utils.ServiceDiscovery – Adding Weaver org.kie.internal.weaver.KieWeaversImpl
    379 [main] INFO org.kie.internal.utils.ServiceDiscovery – Adding Runtime org.drools.beliefs.bayes.runtime.BayesRuntime
    java.lang.ExceptionInInitializerError
    at org.drools.common.AbstractRuleBase.(AbstractRuleBase.java:150)
    at org.drools.reteoo.ReteooRuleBase.(ReteooRuleBase.java:150)
    at org.drools.reteoo.ReteooRuleBase.(ReteooRuleBase.java:127)
    at org.drools.RuleBaseFactory.newRuleBase(RuleBaseFactory.java:86)
    at org.drools.RuleBaseFactory.newRuleBase(RuleBaseFactory.java:74)
    at org.drools.RuleBaseFactory.newRuleBase(RuleBaseFactory.java:37)
    at org.drools.impl.KnowledgeBaseFactoryServiceImpl.newKnowledgeBase(KnowledgeBaseFactoryServiceImpl.java:49)
    at org.drools.KnowledgeBaseFactory.newKnowledgeBase(KnowledgeBaseFactory.java:63)
    at com.sample.DroolsTest.readKnowledgeBase(DroolsTest.java:48)
    at com.sample.DroolsTest.main(DroolsTest.java:23)
    Caused by: java.lang.IllegalArgumentException: Unable to set Conflict Resolver ‘org.drools.core.conflict.DepthConflictResolver’
    at org.drools.RuleBaseConfiguration.determineConflictResolver(RuleBaseConfiguration.java:872)
    at org.drools.RuleBaseConfiguration.init(RuleBaseConfiguration.java:447)
    at org.drools.RuleBaseConfiguration.(RuleBaseConfiguration.java:248)
    at org.drools.RuleBaseConfiguration.(RuleBaseConfiguration.java:162)
    … 10 more

  2. laititia
    13/05/2016 à 09:46 | #2

    Merci pour ce tuto.
    J’ai cette erreur:
    SLF4J: Failed to load class « org.slf4j.impl.StaticLoggerBinder ».
    SLF4J: Defaulting to no-operation (NOP) logger implementation
    SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.
    Exception in thread « main » java.lang.RuntimeException: Unable to get LastModified for ClasspathResource
    at org.drools.core.io.impl.ClassPathResource.getLastModified(ClassPathResource.java:212)
    at org.drools.core.io.impl.ClassPathResource.getInputStream(ClassPathResource.java:149)
    at org.drools.compiler.kie.builder.impl.KieFileSystemImpl.write(KieFileSystemImpl.java:65)
    at com.pojo.ManageExpertSystem.createKieSession(ManageExpertSystem.java:27)
    at com.pojo.Principale.main(Principale.java:28)
    Caused by: java.io.FileNotFoundException: ‘EvaluationDossier.drl’ cannot be opened because it does not exist
    at org.drools.core.io.impl.ClassPathResource.getURL(ClassPathResource.java:173)
    at org.drools.core.io.impl.ClassPathResource.getLastModified(ClassPathResource.java:185)
    … 4 more

    J’ai besoin de votre aide.

  1. 25/09/2015 à 11:49 | #1


× sept = 7