Chapter 7. Human-Readable Rules

The title of this chapter could be offensive to some developers. Aren't all the rules we have covered so far human-readable? Aren't we humans? The idea behind this chapter is to introduce other ways to define rules in Drools that are more user-friendly. In this chapter, "human" means a non-technical person.

So far, we have covered a single way to define rules and knowledge: the DRL language. This language—even if powerful—is inappropriate, most of the time, for users without a technical background. And even then, DRL requires a certain amount of time to become familiar with it. Drools provides other means of knowledge creation by supporting different abstractions over DRL that make the language simpler.

Having a simpler and more concrete language provides us with a great advantage: we can include Subject Matter Experts (SME) in the development cycle. Business requirements no longer have to be translated into technical artifacts (such as classes, rules, and so on) by the developers. The people writing these artifacts can be the people who have business expertise. This is one step towards to the holy grail of business rule engines: business rules written by business people.

This chapter will cover some of the abstractions that Drools provides over DRL, and some other ways in which we can generate DRL from various resource types. The topics included in this chapter are:

  • How to define and use a Domain-Specific Language (DSL) in Drools
  • How to define rules using spreadsheets
  • How to use templates to generate DRL from structured data
  • An introduction on how to use PMML (Predictive Model Markup Language) in Drools

Domain Specific Languages

The first abstraction over DRL that we are going to cover is Domain Specific Languages, or simply DSL. DSL is a great way to tailor DRL for a specific context. In the previous six chapters, we covered all the concepts required to create rules and execute rules in Drools. These concepts require a certain amount of knowledge. For the left-hand side of a rule, we need to understand the DRL syntax and to have an idea of things such as pattern matching, the internal structure of our model, invocation to external systems, and so on. For the right-hand side, we need to know Java and some of the automatic variables present in this context, such as kcontext and drools. Having to master all this knowledge in order to write a business rule seems like overkill, and indeed it is. One of the ways to fill the gap between the technical requirements from DRL and the SMEs is by using a DSL.

The concept behind a Drools DSL is simple: to create a dictionary file containing business-oriented concepts and their translations to DRL. The SME then only has to know about the business-oriented concepts when writing rules without worrying about the technical aspects of DRL.

The dictionary file that defines the translation between business concepts and DRL is simply called DSL in Drools. A file containing rules defined using business concepts is called DSLR. While the technical team is in charge of the creation and maintenance of the dictionary file (DSL), the SME is in charge of the creation and maintenance of the business rules (DSLR).

DSL is supported out of the box by Drools; there are no special dependencies, and no configuration is required before we can start using it.

The Dictionary file

The Dictionary file (or DSL) is a text file (with a .dsl extension) that contains DSL entries. Each line of this file starting with an opening bracket "[" is considered a DSL entry. Lines starting with hash character # are considered comments. Lines starting with something other than an opening bracket or a hash symbol are considered to be part of the previous DSL entry. The format of a DSL entry is as follows:

[<scope>][<type definition>]<dsl expression>=<replacement text>

The <scope> section currently supports four different types:

  • [when] or [condition]: This DSL entry can only be used in the left-hand side of a rule
  • [then] or [consequence]: This DSL entry is only valid for the action part of a rule
  • [*]: This DSL entry is valid in both the condition and action part of a rule
  • [keyword]: This DSL entry is valid in any part of a DSLR file, even outside a rule definition

The <type definition> section is not mandatory (we can either omit it or use empty brackets []) and it is used as a hint for editors such as KIE-Workbench.

After the <type definition> comes the <dsl expression>. This is the text that will be matched and replaced in the DSLR file. A <dsl expression> consists of a Java regular expression (https://docs.oracle.com/javase/8/docs/api/java/util/regex/Pattern.html) and any number of embedded variable definitions. A variable definition is enclosed in braces ("{" and "}") and it consists in a variable name and two optional sections separated by a colon ":". If only one optional section is present, it is used as a regular expression to match text that will be assigned to the variable; if the two optional sections are present, the first one is used as a hint for an editor (such as the KIE-Workbench) and the second one is the regular expression. A <dsl expression> always ends with an equal sign (=).

What follows after the equals sign is the <replacement text> that will be used to replace any part of the DSLR file matching the <dsl expression>. Variables defined in the <dsl expression> section can be used in this section, just like named regular expression matching groups, by using the name of the variable enclosed in braces. Optionally, the variable name may be followed by an exclamation point "!" and a transformation function. The supported transformation functions are detailed in the following table.

Name

Description

uc

Converts all letters to upper case.

lc

Converts all letters to lower case.

ucfirst

Converts the first letter to upper case, and all other letters to lower case.

num

Extracts all digits and - from the string. If the last two digits in the original string are preceded by (.) or (,) a decimal period is inserted in the corresponding position.

a?b/c

Compares the string with string a, and if they are equal, replaces it with b, otherwise with c. But c can be another triplet a, b, c, so that the entire structure is, in fact, a translation table.

A typical (and very simplistic) DSL entry might look like the following:

[when]There is a Customer=Customer()

The previous entry is defining a DSL entry that will replace any occurrence of the text There is a Customer inside a DSLR file with the corresponding DRL Customer().

Adding constraints to patterns

A common requirement when creating a domain-specific language is to have the ability to add an arbitrary number of constraints to a pattern. For example, we may want to be able to express filters, such as a customer with age greater than 40, a customer with GOLD category, and even combined expressions such as a customer in the GOLD category and older than 40 years. Having an individual DSL entry for each possible combination of constraints in our model makes no sense at all. Drools allows us to specify individual constraints for a pattern in DSL and to combine them in any way we want in the DSLR file. This magic is done by appending a hyphen sign "-" to the <dsl expression> section of a DSL entry:

[when]There is a Customer=Customer()
[when]- age greater than {years:d*} years=age > {years}
[when]- category is {category:w*}=category == Customer.Category.{category}

The previous DSL definition allows us to create a rule such as the following in DSLR:

rule "A GOLD Customer older than 31 years"
when
    There is a Customer- age greater than 31 years
        - category is GOLD
then
    ...
end

When a hyphen-prefixed <dsl expression> is processed, its result is not added as a new line in the generated DRL, but instead it is added to the last pattern line preceding it as a constraint. The previous DSLR code will then be translated into the following DRL:

rule "A GOLD Customer older than 31 years"
when
    Customer(age > 31, category == Customer.Category.GOLD)
then
    ...
end

When multiple constraints are present for a single pattern, they are always appended using a comma (,). This means that constraints are always ANDed in DSL.

Hyphens can also be used for DSL entries, which are defined inside the [then] context. In this case, the entry is assumed to be part of a modify statement and its content is added to the previous line (this must be a valid modify statement):

[when]There is a Customer=$c: Customer()
[when]- age greater than {years:d*} years=age > {years}
[then]Update Customer=modify($c){}
[then]- set Category to {category:w*}= setCategory(Customer.Category.{category})

Using the previous DSL, we can write the following rule in DSLR:

rule "Mark Customers older than 31 years as BRONZE"
when
    There is a Customer- age greater than 31 years
then
    Update Customer
        - set Category to BRONZE
end

The resulting DRL from the following example will be:

rule "Mark Customers older than 31 years as BRONZE"
when
    $c: Customer(age > 31)
then
    modify ($c) { setCategory(Customer.Category.BRONZE) }
end

Rules files

A rules file, also referred to as a DSLR file, is a text file with a .dslr extension. DSLR files define Drools rules using a domain-specific language. Previous versions of Drools required the DSLR file to specify which DSL had to be used to process it. This is no longer the case in the current version of Drools; all DSL files included in a KIE Base are going to be used to process any DSLR file also found in that same KIE Base.

The translation of a DSLR file into DRL follows these steps:

  1. Each of the [keyword] entries is applied to the entire DSLR. If the [keyword] contains variables, these variables are captured and replaced by the corresponding value.
  2. The left-hand side and right-hand side of all the rules present in the DSLR are processed. Each of the lines in the DSLR text is matched against each individual DSL entry in the order in which they are defined in the DSL file. This means that the order of the entries in a DSL file matters. When a match is found, the DSLR text is replaced by the corresponding DRL. If the DSL entry defines any variable, its value is taken from the DSLR and copied into the generated text.
  3. If a DSLR line is written with a leading hyphen, the expanded result is added to the previously expanded pattern—if the context is [when]—or to the previously expanded modify statement, if the context is [then].

A detailed explanation of the translation process can be found in Drools' documentation: http://docs.jboss.org/drools/release/6.3.0.Final/drools-docs/html/ch08.html#d0e11300.

By default, plain DRL or Java syntax is not allowed in a DSLR file. However, there is a mechanism to mix DRL and Java sentences along with DSL inside a DSLR file for experienced users. Any line in a DSLR resource starting with a greater than sign > will be skipped by the translator and will remain unmodified in the final DRL resource. This facility provides the means to overcome some limitations of DSL.

DSL troubleshooting

One of the most difficult aspects of working with DSL/DSLR is understanding and fixing errors. The fundamental issue is that errors are reported based on the generated DRL, and will not refer to the high-level DSLR statements used to create the DRL.

There are some methods we can use though to make the detection and correction of errors easier.

The first handy feature we have resides in the DSL itself. Using a special type of comment starting with # we can make Drools log certain information regarding the DSL translation process. This special comment could contain the following keywords that will enable specific features of the DSL debug capabilities:

Keyword

Description

result

Prints the resulting DRL text, with line numbers.

steps

Prints each expansion step of condition and consequence lines.

keyword

Dumps the internal representation of all DSL entries with scope "keyword".

when

Dumps the internal representation of all DSL entries with scope "when" or "*".

then

Dumps the internal representation of all DSL entries with scope "then" or "*".

usage

Displays a usage statistic of all DSL entries.

As an example, we could add the following line to one of the DSL examples in the source bundle:

#/ debug display result, steps and  usage

By adding this line, Drools will log some statistics regarding the usage of each DSL sentence (usage), the conversion steps involved in each DSLR sentence (steps), and the final DRL (result).

Note

Drools will use SLF4j to log the output of the debugging options in DSL. Make sure you have a proper logger configured for the org.drools package.

There is another way we can use to generate DRL from DSL/DSLR for debugging purposes, and it involves the internal classes that Drools' compiler uses for this task: org.drools.compiler.compiler.DrlParser, and org.drools.compiler.lang.dsl.DefaultExpanderResolver. Using a combination of these two classes we can convert DSL and DSLR resources into DRL to analyze what the final result looks like. The following lines will convert some DSL and DSLR resources into plain DRL:

String dslContent = //Get the DSL content from somewhere
String dslrContent = //Get the DSLR content from somewhere
DrlParser parser = new DrlParser();
DefaultExpanderResolver resolver = new DefaultExpanderResolver(new StringReader(dslContent));
String drl = parser.getExpandedDRL( dslrContent, resolver);

After this code gets executed, the drl String will contain the expanded DRL code.

Let's now move to a concrete scenario showing all the topics introduced so far.

A simple scenario

Let's now put it all together in a simple example. Going back to our e-shop, let's assume we want to create some classification rules for our Customers. In this simple example, the classification logic will be based on the age of the Customer. Because we want SMEs to write the rules, we have to provide them with a simple, domain-specific language containing only those expressions that apply to this scenario. The SME will then use this tailored language to express the necessary business logic. Let's assume that the business logic we want to implement is the following:

Age

Category

Between 18 and 21 (inclusive)

N/A

Between 22 and 30 (inclusive)

BRONZE

Between 31 and 40 (inclusive)

SILVER

More than 40

GOLD

The categorization rules must only be applied if the customer doesn't have a category already assigned (meaning that the current category is N/A).

A proposed DSL for the described scenario is shown next:

# Simple DSL example file
[keyword]avoid looping=no-loop true
[when]There is a Customer=$c:Customer()
[when]- with age between {low:d*} and {high:d*}=age >= {low}, age <= {high}
[when]- who is older than {low:d*}=age > {low}
[when]- without a Category set=category == Customer.Category.NA
[then]Set Customer Category to {category:w*}=modify($c){ setCategory(Customer.Category.{category}) };

The first sentence after the initial comment is a [keyword] that will allow us to use a more user-friendly alternative for the no-loop attribute in our rules. Following the [keyword] we find four [when] sentences. These sentences will allow us to write the necessary rules for the scenario we are dealing with. The last sentence is a [then]. This sentence can be used to set a category to the customer. The braces belonging to the modify statement have to be escaped ({ and }) so they don't conflict with a variable reference.

The following DSLR rule is only one of the four required rules the SME needs to write. A complete implementation and unit test of this scenario can be found in the source bundle associated with this chapter:

rule "Categorize Customers between 22 and 30"
avoid looping
when
    There is a Customer
        - with age between 22 and 30
        - without a Category set
then
    Set Customer Category to BRONZE
end

After the previous rule gets translated into DRL, the result will look like the following:

rule "Categorize Customers between 22 and 30"
no-loop true
when
    $c: Customer( age >= 22, age <= 30, category == Customer.Category.NA)
then
    modify($c){ setCategory(Customer.Category.BRONZE) }
end

We can clearly identify which of the previous 2 rules is more business-oriented.

One of the limitations of DSL is that SMEs still need to know what the available sentences are and how they can interact with each other. They still need to manually write the rules in a text editor. But one of the advantages of DSL is that, by having a limited set of sentences, it is easy to build a tailored UI for the SMEs to use. Both the Drools Eclipse plugin and KIE-Workbench have support for DSL and DSLR creation and usage. These two applications are, unfortunately, beyond the scope of this book.

As mentioned before, the source bundle included with this chapter contains a complete implementation of the previously described scenario as well as a more elaborated scenario, where the categorization of the Customers is based on the number of Orders they have.

Categorization rules allow us to introduce the next topic in this chapter.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.135.190.182