Simple pattern de service de règles avec Drools

  • Sharebar

Lorsque l’on utilise une API pour la première fois, on regarde souvent les exemples pour voir comment le code s’articule, et on s’aperçoit souvent que l’on trouve des classes ou des méthodes monolithiques qui font un peu tout à la fois et on a parfois du mal a savoir qui fait quoi et pourquoi.

Ces exemples sont toujours très instructifs pour savoir quels sont les appels de base de l’API. Seulement une fois que vous maitrisez un peu plus l’API il devient vite nécessaire de réécrire ces exemples de manière à produire du code un peu plus évolué pour votre application.

Ici , voici l’exemple type généré par le plugin Drools lors de la création d’un projet Drools dans l’IDE Eclipse.

    /**
    * This is a sample class to launch a rule.
    */
    public class DroolsTest {

    public static final void main(String[] args) {
    try {
    // load up the knowledge base
    KnowledgeBase kbase = readKnowledgeBase();
    StatefulKnowledgeSession ksession = kbase.newStatefulKnowledgeSession();
    KnowledgeRuntimeLogger logger = KnowledgeRuntimeLoggerFactory.newFileLogger(ksession, “test”);
    // go !
    Message message = new Message();
    message.setMessage(“Hello World”);
    message.setStatus(Message.HELLO);
    ksession.insert(message);
    ksession.fireAllRules();
    logger.close();
    } catch (Throwable t) {
    t.printStackTrace();
    }
    }

    private static KnowledgeBase readKnowledgeBase() throws Exception {
    KnowledgeBuilder kbuilder = KnowledgeBuilderFactory.newKnowledgeBuilder();
    kbuilder.add(ResourceFactory.newClassPathResource(“Sample.drl”), ResourceType.DRL);
    KnowledgeBuilderErrors errors = kbuilder.getErrors();
    if (errors.size() > 0) {
    for (KnowledgeBuilderError error: errors) {
    System.err.println(error);
    }
    throw new IllegalArgumentException(“Could not parse knowledge.”);
    }
    KnowledgeBase kbase = KnowledgeBaseFactory.newKnowledgeBase();
    kbase.addKnowledgePackages(kbuilder.getKnowledgePackages());
    return kbase;
    }


Il se passe ici plusieurs choses :
1 : initialisation de la knowledge base ou base de connaissance avec un fichier de règles.
2 : Création d’un session à partir de cette knowledge base
3 : Insertion d’un objet dans la mémoire de travail de la session.
4 : Exécution des règles sur cette session.

Ici, je me propose de fournir un bout de code, trois classes exactement afin de pouvoir définir un service (simple) en utilisant l’API drools et en séparant les objectifs de chaque classe.

La première classe initialisera la knowledge base. A partir de celle ci on pourra obtenir des sessions knowledgeBaseSessions.
C’est pourquoi j’ai nommé cette classe ruleSessionProvider. Elle permettra de fournir des sessions qui pourront être ensuite utilisées pour l’exécution de règles sur des faits. On pourra noter que cet exemple permet d’utiliser un ruleflow pour orchestrer l’exécution des règles.

RuleSessionProvider

    /**
    * This class is a singleton and provides sessions factories for executing rules against.
    *
    * Constructor initializes the knowledge base, using a DRL file and a ruleflow file.
    * This class wraps a knowledge base for providing sessions.
    * The first time getInstance is called, it initializes once for all the knowledge base
    * and it can throw an unchecked exception in case there is something wrong with rule related files.
    */
    public class RuleSessionProvider {

    private static final RuleSessionProvider instance = new RuleSessionProvider();

    private boolean initialized = false;
    private KnowledgeBase kbase = null;

    public static RuleSessionProvider getInstance(){
    return instance;
    }

    public StatelessKnowledgeSession getStatelessSession(){
    if(initialized){
    return kbase.newStatelessKnowledgeSession();
    }
    else
    return null;
    }

    public StatefulKnowledgeSession getStateFullSession(){
    if(initialized){
    return kbase.newStatefulKnowledgeSession();
    }
    else
    return null;
    }

    private RuleSessionProvider(){
    initialized = initializeEngine();
    if(initialized == false){
    throw new ConfigurationException(“Unable to initialize rule Engine, please check rule files configuration”);
    }
    }

    private boolean initializeEngine() {
    if(initialized)
    return true;
    return initializeEngineFromDrlFile();
    }

    private boolean initializeEngineFromDrlFile() {
    boolean initOk = true;

    String ruleFileName = “rules.drl”;
    String ruleFlowFileName = “ruleflow.rf”;

    KnowledgeBuilderConfiguration config = KnowledgeBuilderFactory.newKnowledgeBuilderConfiguration();
    KnowledgeBuilder kbuilder = KnowledgeBuilderFactory.newKnowledgeBuilder(config);

    // add rule file to KnowledgeBuilder
    kbuilder.add(ResourceFactory.newFileResource(ruleFileName), ResourceType.DRL);
    KnowledgeBuilderErrors ruleFileErrors = kbuilder.getErrors();
    if (ruleFileErrors.size() > 0){
    initOk = false;
    for (KnowledgeBuilderError error: ruleFileErrors){
    showError(“”+error);
    }
    showError(“Check rule file “+ruleFileName);
    return false;
    }
    else{
    // add ruleflow file to KnowledgeBuilder
    kbuilder.add(ResourceFactory.newFileResource(ruleFlowFileName), ResourceType.DRF);

    KnowledgeBuilderErrors ruleFlowErrors = kbuilder.getErrors();
    if (ruleFlowErrors.size() > 0){
    initOk = false;
    for (KnowledgeBuilderError error: ruleFlowErrors){
    showError(“”+error);
    }
    showError(“Check ruleFlow File “+ruleFileName);
    return false;
    }
    else{
    // KnowledgeBase can be created now
    kbase = KnowledgeBaseFactory.newKnowledgeBase();
    kbase.addKnowledgePackages(kbuilder.getKnowledgePackages());
    }
    }
    return initOk;
    }

    private void showError(String msg){
    System.out.println(msg);
    }
    }

La deuxième classe (RuleServiceProvider) permettra de lancer l’exécution des règles.
Elle crée une session grâce au RuleSessionProvider, insère les faits en working memory, et démarre l’exécution des règles sur ces faits.
On pourra noter egalement qu’il est possible d’utiliser deux types de sessions, pour une execution unique pour une session, et a cet effet on remarquera la différence des appels de l’API pour démarrer l’exécution des règles en parallèle avec le démarrage du ruleflow.

RuleServiceProvider


    /**
    *
    * This class provides the execution layer for executing rules.
    * It uses the RuleSessionProvider to create sessions,
    * in order to start a ruleflow process and start rules execution.
    * This class demonstrates the use of
    * stateful and stateless sessions to run rule sessions.
    *
    */

    public class RuleServiceProvider {

    private String ruleflowId = “ruleflowID”;

    public boolean runStatefulSession(Object[] facts){

    StatefulKnowledgeSession ksession = RuleSessionProvider.getInstance().getStateFullSession();
    if(ksession == null){
    throw new ConfigurationException(“Rule Engine is not initialized, unable to fire rules”);
    }
    for ( int i = 0; i < facts.length; i++ ){
    ksession.insert( facts[i]);
    }

    // start ruleflow process
    ksession.startProcess(ruleflowId);

    // execute rules
    int nbRules = ksession.fireAllRules();

    // cleanup session
    ksession.dispose();
    return true;
    }

    public boolean runStatelessSession(Object[] facts){

    StatelessKnowledgeSession ksession = RuleSessionProvider.getInstance().getStatelessSession();
    if(ksession == null){
    throw new ConfigurationException(“Rule Engine is not initialized, unable to fire rules”);
    }

    List commands = new ArrayList();
    for ( int i = 0; i < facts.length; i++ ){
    commands.add( CommandFactory.newInsert(facts[i]));
    }

    commands.add( CommandFactory.newStartProcess(ruleflowId));
    ksession.execute( CommandFactory.newBatchExecution( commands ) );
    return true;
    }

    }


La troisième classe (RuleService) fournit l’interface d’un service de règle et utilise le RuleSesssionProvider pour l’exécution de règles sur les faits qu’elle lui passe. En soit cette classe n’apporte rien ici, si ce n’est qu’elle permet de cacher le type d’implémentation utilisé pour exécuter de règles.

RuleService


    public class RuleService {

    boolean runService(Object[] facts){
    try {
    RuleServiceProvider sp = new RuleServiceProvider();
    return sp.runStatefulSession(facts);
    //return sp.runStatelessSession(facts);
    }

    catch(ConfigurationException ce){
    System.out.println(“Initialization error when trying to execute runService “);
    ce.printStackTrace();
    return false;
    }

    catch (Exception e) {
    System.out.println(“Unexpected error when trying to execute runService “);
    e.printStackTrace();
    return false;
    }
    }
    }

Au final, on a de-couplé l’initialisation de l’exécution du moteur de règle, afin de distinguer fonctionnellement les différentes parties de base de l’API Drools et je l’espère rendre plus claire l’utilisation de cette API.

This entry was posted in Divers and tagged , . Bookmark the permalink.

Leave a Reply