Saturday, December 11, 2010

Using JBoss Rules (Drools) in Scala


The JBoss Rules Framework, more often referred to as "Drools", is an excellent tool for combining Rules, Workflow, and Events into what the framework authors call "the Business Logic Integration Platform" (BLIP) [1].  We use Drools extensively on our project; the framework enabled us to quickly construct a rich Event Driven Architecture (SOA 2.0) in a very short time frame.  In fact, we like Drools so much, we've been considering using it in the realm of NLP as a way for applying rules to Entity Extraction and Resolution (I should mention this was originally John's idea).

The downside to Drools is that the framework is written in Java.  Sure, Mark Proctor and gang have put a lot effort in making the platform accessible to other languages through web services, but this is far from the level of integration I would enjoy (like a C# version).  I think I should take the time an caveat the fact that I consider myself to be a Java programmer (first and foremost) and then a C# developer, but after a couple of solid weeks in .NET, I find it difficult to go back.  The one thing I do love about Java (more than anything else) is the language's amazing Open Source community.  So instead of embracing the "Dark Side" and becoming a Microsoft evangelist, I have decided to look into the newer languages written for the JVM.

The two JVM languages I find the most intriguing are Clojure and Scala.  Both offer a number of benefits over Java, especially in the area of concurrency (probably one of the most important features for my line of work).  Of course, both also have their disadvantages.  Clojure has proven very difficult to learn, at least for me, since I do not have a solid LISP background.  Scala feels much more natural,  after a couple of chapters of Martin Odersky's book on the language [2] I was writing some minor applications, but it does have some interoperability issues with Java.   For instance, Scala cannot access static properties of Java objects [3].

Since I like both languages, I wanted to see how compatible they were with Drools.  In this post (sorry for the long introduction), I will demonstrate how Scala can be used to not only run the Drools engine, but also be used to define domain objects that can be used within the engine.

I was a meteorologist in another life, so I'm going to draw from that domain.  Let me start by introducing our one, and only, domain model object "Temperature".

package com.berico.model.weather

trait Temperature {
 def value : Double
}

I admit that this is a terrible example of a domain object because I am not constraining the "value" to a specific temperature unit.

For the purpose of this demo, we are going to write some rules (in Drools Expert) that will evaluate this temperature to determine if we need to declare a "heat warning" or a "freeze warning".  The Rule Definition (a text file called "WeatherRules.drl" is listed below.

package com.berico.rules
 
import com.berico.model.weather.Temperature

rule "Too Hot"
   dialect "mvel"
   when
      temp : Temperature( value > 85 )
   then
      System.out.println(
        temp.value.toString() + " F is too hot."
        + " Declare HEAT WARNING!"); 
end

rule "Too Cold"
   dialect "mvel"
   when
      temp : Temperature( value < 32 )
   then
      System.out.println(
         temp.value.toString() + " F is too cold."
         + " Declare FREEZE WARNING!"); 
end

We have now established that if a temperature greater than 85 degrees, or less than 32, is inserted into the Drool Knowledge Session, we should see an message declaring a HEAT WARNING or FREEZE WARNING, respectively.

The next step is to load the rule file (WeatherRules.drl) into the Drools runtime.  We'll wrap this logic into a function that returns the StatefulKnowledgeSession, which is primary object you use to insert facts into and run rules against in the Drools API.

def GetKnowledgeSession() : 
          StatefulKnowledgeSession = {
   
   // Get a new knowledge builder instance
   var kbuilder : KnowledgeBuilder 
      = KnowledgeBuilderFactory
          .newKnowledgeBuilder()

   // Add our ruleset (which will be parsed 
   // and compiled) to the knowledge builder
   kbuilder.add(ResourceFactory
      .newClassPathResource("WeatherRules.drl"),
         ResourceType.DRL)

   // Create a new knowledgebase
   var kbase : KnowledgeBase 
     = KnowledgeBaseFactory.newKnowledgeBase()

   // Add the compiled rule sets and workflows 
   // into the knowledgebase
   kbase.addKnowledgePackages(
      kbuilder.getKnowledgePackages())

   // Create the knowledge session
   var ksession : StatefulKnowledgeSession 
      = kbase.newStatefulKnowledgeSession()

   // If you want to see what's going on within
   // the Drools Engine just uncomment this line
   //var logger : KnowledgeRuntimeLogger = 
   //  KnowledgeRuntimeLoggerFactory
   //    .newConsoleLogger(ksession)

   // Return the knowledge session
   ksession
} 

Finally, we will retrieve the StatefulKnowledgeSession, insert two Temperature objects (called facts in Drools) and fire the ruleset.

def main(args : Array[String]) : Unit = {

   println("Creating Knowledge Session")
   
   // Retrieve the knowledge session
   var ksession : StatefulKnowledgeSession 
      = GetKnowledgeSession()
    
   println("Creating and insertng Temperature")
    
   // Here is our first temperature object
   val shouldBeTooHot = new Temperature {
      def value = 100 
   }
    
   // Here is our second temperature object
   val shouldBeTooCold = new Temperature {
      def value = 20
   }
    
   // Insert the temperatures into the 
   // knowledge session
   ksession.insert(shouldBeTooHot)
   ksession.insert(shouldBeTooCold)
    
   println("Firing all rules")
    
   // Fire the rules
   ksession.fireAllRules()
}

That's it. The output at the console should read (copied this from Eclipse):

Creating Knowledge Session
Creating and insertng Temperature
Firing all rules
20.0 F is too cold. Declare FREEZE WARNING!
100.0 F is too hot. Declare HEAT WARNING!

As you can see, it is completely possible to use Scala in conjunction with JBoss Rules, and not necessarily as a second-class citizen.

In a later article, I hope to demonstrate the exact same tutorial in Clojure.

Rich

Resources:

Eclipse Project  http://dl.dropbox.com/u/12311372/DroolsScala.zip

References:

[1].  http://www.jboss.org/drools
[2].  http://www.artima.com/shop/programming_in_scala
[3].  http://www.scala-lang.org/faq/4

8 comments:

  1. I love Scala !

    An "upside" of using Java programming language is it's much easier to work with EMF and modeling in general. But it has more to do with EMF support for Java rather than the strength of Java (which technically has no advantage to Scala).

    Another is that ScalaIDE plugin has still a long way to catch up with JDT.

    I would love to see more Scala adoption and better tooling for Scala. Especially since the recent tragedies with Java... Scala is a serious contender to C#, or better better than C#. Though I don't mind using Java7 ;-)

    Sorry for OOT comment ;)

    ReplyDelete
  2. Henry,

    Thanks for your comments. I've really enjoyed Scala as a language. It's certainly far easier to use for someone coming from an OOP background than Clojure. I too am a little disappointed at IDE support for both languages, but then again, both languages are young and I have no room to complain since the current offerings are open source.

    As for C#, I really like the language, and am definitely more productive in it. Of course, the Java Community is much richer and frameworks like Drools, Vaadin, Google App Engine, etc. keep me coming back.

    ReplyDelete
  3. Great post, thanks Richard. Have you used Scala further with Drools? Have you run into any issues?

    ReplyDelete
  4. @Nicholas: A little bit more, but nothing in production. I do a lot of work with Drools, and have been looking for a new JVM-based language, so I decided to try Drools with Scala. Have you encountered any issues?

    ReplyDelete
  5. Hi,

    I am working on integrating Drools with scala2.9.0.1. I am trying the above mentioned example, its compiling and running without error, but it is not showing any rule's output. I am using Drools 5.2.0 which is the current stable release. Please help me to resolve my problem.

    Puneet

    ReplyDelete
  6. Puneet,

    Not sure how I can help without an error or stack trace. There are a number of handlers in Drools (for Processes and Rules) you can implement to trap events occurring within the API. I believe the Drools team includes a handler that will output events to the console. Try adding one of these handlers to ensure the process or rules are firing. If that doesn't work, would you mind posting me a link to your code?

    Thanks,

    Richard

    ReplyDelete
  7. Thanks for this guide!

    I tried the above with the REPL of scala version 2.10.2 and when running "GetKnowledgeSession" first time, got this error:

    warning: Class com.sun.tools.xjc.Options not found - continuing with a stub.
    error: error while loading KnowledgeBuilderFactory, class file '/home/ingvar/.ivy2/cache/org.drools/knowledge-api/jars/knowledge-api-5.5.0.Final.jar(org/drools/builder/KnowledgeBuilderFactory.class)' is broken
    (class java.lang.NullPointerException/null)

    when I run it the second time, error does not appear (but firing rules doesn't give any results).

    as hinted by Antoine Gourlay here:
    http://comments.gmane.org/gmane.comp.lang.scala.simple-build-tool/8907
    xjc.Options is no longer part of tools.jar

    it must be added explicitely (i.e. in the build.sbt):
    libraryDependencies += "com.sun.xml.bind" % "jaxb-xjc" % "2.2.4-1"

    best
    Ingvar







    ReplyDelete
    Replies
    1. I have tried the above one, after facing of lot of classpath issues main run with out error, But no rules fired

      Delete