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

I love Scala !
ReplyDeleteAn "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 ;)
Henry,
ReplyDeleteThanks 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.
Great post, thanks Richard. Have you used Scala further with Drools? Have you run into any issues?
ReplyDelete@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?
ReplyDeleteHi,
ReplyDeleteI 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
Puneet,
ReplyDeleteNot 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