javadrools

Generate documentation from programmatic drools rules


I'm working on a project which uses Drools to validate certain java objects. We have lots of these rules now, and even though many were historically created from decision tables, many are not. This makes it hard to get an overview of which rules actually apply to a specific type of object.

I was thinking I could automatically generate some sort of documentation from the programmatic .drl rules. After some research, I've managed to extract a lot of useful information from the LHS (when clause) programmatically like this:

RuleImpl rule = KieContainer.getKieBase().getKiePackages().get(index).rules.get(ruleIndex)
GroupElement whenGroup = rule.getLhs()

I think this is enough for the "when:" clause. However, it seems like Drools does not have much information on the "then:" clause available programmatically. It should be accessible from rule.consecuence, but from what I've gathered it doesn't really tell me much more than what class of object is created (I really need to know it's parameters). For example, in the following case:

then
        insert(new Valideringsfejl("rta014.5", FejltypeHelper.MAA_IKKE_ANGIVES, "planlagtKapacitetIkkeAngivetSol"));

I am interested in the arguments "rta014.5", FejltypeHelper.MAA_IKKE_ANGIVES and "planlagtKapacitetIkkeAngivetSol". So is there a way to get this information without parsing the source code myself, or can this kind of documentation be generated in any other way?


Solution

  • This is a very naive approach, because rules can become very complex. While yours might be on the simpler side, there is no out-of-the-box solution for this because of this complexity. The best you might be able to do for a general solution is a third-party tool to generate graphs that trace the old rete trees in Drools 5 or so. (I built one of those tracing tools as an intern; it was one of the hardest projects of my career and the end result wasn't particularly useful.)

    The other reason it wouldn't make sense for such a tool to exist in the general sense is that every rule base is inherently tied to the application its supporting. From your post, I see that your rules rely heavily on inserting facts into working memory -- and that's what you're most interested in. I have supported rulesets with 10,000+ rules which never call insert at all. An insert-based solution which would satisfy your use case, would be useless for my application.


    So you're left with going down the route of writing your own parser which -- honestly -- doesn't sound like such a terrible prospect.

    If all you want to know is the name of the rule and the 'insert' values on the right hand side ("then"), I figure there's at least three ways to do this:


    There is one more approach which you have not considered. When I ran into a similar situation with a legacy ruleset (~40,000 rules) which were terribly complex from a business perspective (eg. after 10 years in the industry I still couldn't tell you if what it was doing was correct), but not so much from a technical implementation perspective, I actually used my unit tests to document the rulesets so my Business Analysts could understand what I was actually doing (and verify the rules for us.)

    To do this I used BDD (behavior driven testing) and a language called Gherkin which described the rule, its triggers, and its consequences in plain English. Then I used the testing tool Cucumber to actually execute those Gherkin test cases as-is by firing the rules. In that way I had both a test and a human-readable description for each rule, and since these were treated and executed as unit tests, any modification to the rule which would violate its human-readable contract would cause the test to fail (thus requiring it to be updated.) Win-win, from our perspective. But I also had 3 interns to help me write those 40,000 Gherkin test cases.

    I have a toy project demonstrating how these technologies work together on GitHub if you're interested in this approach. Unfortunately I don't have examples of the other approaches that I presented because -- as I discussed previously -- such solutions would not be particularly general and would be so application-specific to be practically useless outside of that context.