Drools – Tutorial on Writing DSL Template

Few months ago I wrote a post that describes an example that uses source DRL in conjunction with DSL template. In the current post, I want to describe and show with few examples how to write DSL template – whats allowed and whats not.

Now, having said that, I want to say that I am not planning to repeat the whole JBoss Rules manual – the Drools team done a great job. I just want to share from my personal experience, what I have come across while working with Drools when writing DSLs.

There are few upcoming changes coming in the future release of Drools (version 5) in terms of DSL. For example DSL will become more powerful in terms of regular expressions – it will be possible to include regexp in the DSL tokens. Edson Tirelli posted an article written by Matt Geis on Drools Blog, that talks about it and gives few examples.

Since I am working with Drools 4.0.7 these days, I will describe in the current post how to write DSLs for Drools 4.0.7.

Within a scope of Drools, DSL’s job to map (expand) DRL expressions written in natural language into underlying programming code.

When writing DSL there several basic DO’s and DONT’s you should be aware of:

  1. Don’t put closing semi-column at the end of DSL expression line:
    [when]Message status is {1} = m : Message(status == {1});
  2. Avoid different token names on the left hand side (before the “=”) and right hand side (after the “=”) of the DSL expansion:
    [when]Message status is {3} = m:Message(status == {1})
    [then]Log “{message}” = System.out.println(“{bobo}”);
  3. DSL expansion should be on one line. You can even have several expressions on the same line, but you cannot break the line. By putting expression on a new line will cause an exception during parsing:
    [when]Message id is {id} = m:Message(id == {id})
    [then]LogUpdate “{2}” = System.out.println(“{2}”);
    update(m);
    [when]Message status is {s} = m:Message(status == {s})
    [then]Log “{2}” =
    System.out.println(“{2}”);
  4. Avoid having DSLs with the same name. It can create a confusion. Remember that DSL is parsed from top to bottom, so when two DSLs with the same name exist, the top one will apply during expansion since it will be matched first:
    [then]SetStatus {s} = System.out.println({s});
    [then]SetStatus {s} = m.setStatus({s});
  5. If token has quotes on the left hand side of the DSL, then the token must have quotes on the right hand side of the DSL:
    [then]SetMessage {msg} = m.setStatus({msg});
  6. There is no room for typos:rule “set-hello”
    when
    True
    then
    SetMessage “Hello”
    end[then]SetMessag “{msg}” = m.setStatus(“{msg}”);
  7. Token names can be literal or numeric characters, or both:
    [then]Print1 “{1}” = System.out.println(“{1}”);
    [then]Print2 “{msg}” = System.out.println(“{msg}”);
    [then]Print3 “{23msg}” = System.out.println(“{23msg}”);
  8. If token on the left hand side has no quotes, and expression/function on the right hand side can accept parameter of type String, then the token can be placed between quotes on the right hand side:
    [then]PrintInteger {1} = System.out.println({1});
    [then]PrintInteger2 {1} = printingFunction({1});
  9. If you want to use declared variable in right hand side, make sure it is declared on the left hand side of the rule. Remember, DSL template is ONLY a template, it is a not a place to declare variables, declaration occurs only in DRL, DSL contains the expansion. Keep in mind that declaration and usage of the variable is per-rule basis:rule “set-hello”
    when
    Message
    then
    SetMessage “Hello”
    end[when]Message = m : Message() //declare and initialize ‘m’
    [then]SetMessage “{msg}” = m.setStatus(“{msg}”);
  10. White spaces before the “=” and after are allowed when writing a DSL line:
    [then]SetMessage “{msg}” = m.setStatus(“{msg}”);
  11. If you are using a function in the right hand side of the DSL of the expansion, then make sure you provide necessary imports in DRL source for all the objects the function is using. For example if your function gets a proxy to a JMS Queue from JBoss, then you have to include imports for all the objects associated with this operation, like you normally would do it in normal Java class. Also the function itself must be present in source DRL.
  12. Left hand side of the rule, can evaluate boolean function instead of expression:rule “CheeseType”
    when
    Cheese is of type “silton”
    then
    DoSomething
    endfunction boolean someEvaluatingFunction(Cheese cheese,String type) {
    if (cheese.getType().equals(type)) return true;
    else return false;
    }

    [when]Cheese is of type “{1}” = chz:Cheese() eval(someEvaluatingFunction(chz, “{1}”))
    [then]DoSomething = System.out.println(“Something”);

If you interested in additional examples of the DRL+DSL, you can have a look at another short example that I wrote sometime ago. Also, please dont forget to have a look at the JBoss Rules manual that i mentioned earlier.

Cheers