Skip to content

Accessing Drools 5 rules via a restful API using Drools Server and Groovy RestClient

If you haven’t heard about drools, you should have a look at it now. It provides multiple ways to write rules: DSL (drl files), groovy, java, excel spreadsheets or via a web interface (Guvnor). I recommend the book, Drools – JBoss Rules 5.0 by Michael Bali as its the only comprehensive documentation available on drools.

One of the lesser known features but extremely powerful is the Drools server that is provides a restful api to access and invoke a rules knowledge base. This is a war available in the Drools binary distribution. You would place the exploded version of this in your server (tomcat 6, in my case) and
— add the following jars in the war: drools-decisiontables.jar, drools-templates.jar, jxl.jar, commons-lang.jar, joda-time.jar

I’m using the same example used in the book. For the source code download here. Copy the following into Drools Server classpath retaining the package structure

— model classes from decision_tables project (droolsbook/decisiontables/bank/model/*)
— model classes from bankingcore project (droolsbook/bank/model/*) retaining the package structure
— add interestcalculation.properties in the classpath (WEB-INF/classes). Add the following
— newInstance = true
— file = -- copy interestcalculation.xls from decision_tables project (drools-book/decision_tables/src/main/resources)

Accessing a restful url with a standard request object that looks like:


<knowledgebase-request>
  <globals>
    <named-fact>
      <id>myglobal</id>
      <fact class="org.drools.server.ExampleFact">
        <carPrice>42</carPrice>
        <carType>Saab</carType>
      </fact>
    </named-fact>
  </globals>
  <inOutFacts>
    <named-fact>
      <id>myfact</id>
      <fact class="org.drools.server.ExampleFact">
        <carPrice>50</carPrice>
        <carType>BMW</carType>
      </fact>
    </named-fact>
  </inOutFacts>
  <inFacts>
    <anon-fact>
      <fact class="org.drools.server.ExampleFact">
        <carPrice>55</carPrice>
        <carType>Audi</carType>
      </fact>
    </anon-fact>
    <anon-fact>
      <fact class="org.drools.server.ExampleFact">
        <carPrice>65</carPrice>
        <carType>Mercedes</carType>
      </fact>
    </anon-fact>
  </inFacts>
  <queries>
    <query-type>
      <args>
        <string>one</string>
        <string>two</string>
      </args>
      <factNames>
        <string>ninsf</string>
      </factNames>
      <queryName>Get named inserted fact</queryName>
    </query-type>
  </queries>
</knowledgebase-request>

You would get a standard response back that looks like this:


<knowledgebase-response>
  <globals>
    <named-fact>
      <id>myglobal</id>
      <fact class="org.drools.server.ExampleFact">
        <carPrice>42</carPrice>
        <carType>Saab</carType>
      </fact>
    </named-fact>
  </globals>
  <outFacts>
    <named-fact>
      <id>ninsf</id>
      <fact class="org.drools.server.InsertedFact">
        <name>one</name>
      </fact>
    </named-fact>
    <named-fact>
      <id>ninsf</id>
      <fact class="org.drools.server.InsertedFact">
        <name>two</name>
      </fact>
    </named-fact>
  </outFacts>
  <inOutFacts>
    <named-fact>
      <id>myfact</id>
      <fact class="org.drools.server.ExampleFact">
        <carPrice>50</carPrice>
        <carType>BMW</carType>
      </fact>
    </named-fact>
  </inOutFacts>
</knowledgebase-response>

Chapter 11 of Michael Bali's book explains an example using a Ruby client. I'm going to explain with a Groovy client, instead.

import groovyx.net.http.*
import groovyx.net.http.ContentType
import groovy.xml.MarkupBuilder

droolsClient = new RESTClient( 'http://localhost:8080/drools-server-5.0.1/' )
	 
def accountStr = new StringWriter()
def account = new groovy.xml.MarkupBuilder(accountStr)
account.'knowledgebase-request'(){
	inOutFacts() {
		'named-fact'() {	
			id("account")
			fact(class:'droolsbook.decisiontables.bank.model.Account') {
				type("STUDENT")
				balance("1000")
				currency("EUR")
			}
		}
	}
}

println 'printing xml ... '
println accountStr

resp = droolsClient.post( path : 'knowledgebase/interestcalculation', 
						  body : accountStr.toString(),
						  contentType: ContentType.XML,
						  requestContentType: ContentType.XML)
 

println "printing response ..."
println resp.data

The above code connects to drools server running on tomcat and expects a interestcalculation.properties rulebase, compiles the knowledgebase and fires all rules returning a response. In this example a student with a balance of 1000 EUR is returned with a 1.00 interest rate from the rule.

I ran into several problems while writing this simple groovy class (thanks to Groovy's horrible exception handling). For instance if I use '/knowledgebase/interestcalculation' instead of 'knowledgebase/interestcalculation' I get a strage error:
org.xml.sax.SAXParseException: The element type "HR" must be terminated by the matching end-tag "".

And if you don't provide contentType: ContentType.XML or requestContentType: ContentType.XML, you get a:
java.lang.NullPointerExceptionat groovyx.net.http.HTTPBuilder$RequestConfigDelegate.setBody

Finally, once you get everything sorted out, the combination of a drools restful client and drools server seems to me an ideal solution to develop, deploy and release drools independently from any application. In fact the application need not know about rules or any of its configuration apart from the restful contract. Quite remarkable.

[Slashdot] [Digg] [Reddit] [del.icio.us] [Facebook] [Technorati] [Google] [StumbleUpon]

One Comment

  1. Plugtree wrote:

    Great post. Michael’s book is really comprehensive too. This solution provides decoupling for enterprise architectures using a decision server like Drools.

    Thursday, December 3, 2009 at 3:19 pm | Permalink

Post a Comment

Your email is never published nor shared. Required fields are marked *
*
*