Principles of Software Flow


Creating a Drools Playground with Fitnesse

Playground at Fuji-Hakone-Izu National Park

/Playground at Fuji-Hakone-Izu National Park

Who sucks all the joy out of Drools?

There it is, this exciting new technology that I’m keep to play with.  I’m eager to play with it but the documentation makes me wade through countless pages about  building, deploying, utilizing and running.  Yawn, that’s no fun.

When it comes to learning I’m with Forth creator’s Charles Moore “Fun Down” approach:

I’ll do the thing that’s most fun in order to get into the problem. If I have to clean up all those details later, that’s the price I pay.

Ian Brodies’ “Thinking Forth” page 91 

I don’t want to spend ages setting the thing up.  I just want to try stuff.  See what works, what doesn’t.  I want to suck it and see.  All this seriousness just sucks.

I’m not going to be deterred by it.  As a general rule anything that tries to be fun is dull and anything that tries this hard to be serious must be great fun.  The name, Drools, shows the playfulness of the creators.  The communities rejection of the JBoss rules brand shows that the playfulness lives on.  The frowns and tuts tuts of some cannot discourage it.

Consider, for example, Marc Proctor’s Pong Game in 13 minutes on You Tube: https://www.youtube.com/watch?v=Omj4PR3v-nI

Build_Pong_in_13_minutes_using_JBoss_Drools_-_YouTube

Here we see the playfulness.  It was an opportunity to show just how disruptive a technology Drools can be, breaking all the rules in order to achieve something unexpected.  As was explained further in the comments:

Mark Proctor n reply to Rick Kilcoyne:

I hope these videos show that rule engines are more than just “if” statements, and the declarative programming model is quite powerul and more general puprose than people probably realise.

Frowns and tut tuts?  Of course.  Consider the following comments from You Tube:

detour98:
Mark, what you’ve just showed is one of the most insanely idiotic things I have ever seen. At no point in your copy pasting, alt tabbing, incoherent code were you even close to anything that could be considered a 13 minute build of anything. Everyone in this video comments is now dumber for having spent 14:39min of their lives watching it. I award you no points, and may God have mercy on your soul.

lukeyd13:

This shit doesnt compile at all?!!!

Does any of that distract from how fascinating the example is?  Not at all.  It is a simple example that provides a gilmpse of how things could be done differently.  This isn’t lost on everybody:

Rick Kilcoyne in reply to Mark Proctor:
Thanks! And well done.

Paul St. Amant:
You are awesome. I thought I was watching an episode of Lost. And I loved Lost.

MeanMachine11111:
super amazing

While at work I will use Drools in an appropriately serious manner for proven use cases.  At home I’m going to play with Drools and have fun breaking some rules.  I’m going to follow the example set by Mr Proctor and his 70s video game.

In order to enjoy Drools I’m going to have to create a playground for myself.  Somewhere I can safely mess about with the core engine without having my flow disrupted by the having to wait for builds and deployments.

For building a playground my tool of choice is Fitnesse.

Getting Playful with Fitnesse

In contrast to Drools chapters here is what you need to do to get up and running with Fitnesse:

It really takes very little time and effort to get FitNesse running on your machine.

Click on the most recent fitnesse-standalone.jar file.

  • If this is a brand new installation, just put it in some convenient empty directory.
  • If this is an update, then copy it on top of the old fitnesse(-standalone).jar in your current installation.

Type java -jar fitnesse-standalone.jar

  • It will ask you to be patient as it installs or updates.
  • Your shell should respond with something like this:
        FitNesse (v20130228) Started...
         	port:              8001
         	root page:         fitnesse.wiki.FileSystemPage at ./FitNesseRoot
         	logger:            none
         	authenticator:     fitnesse.authentication.PromiscuousAuthenticator
         	page factory:      fitnesse.responders.PageFactory
         	page theme:        bootstrap
         	page version expiration set to 14 days.

Start up a browser and go to http://localhost

The main FitNesse screen should come up. That’s it. You’re ready to start using FitNesse.

The Download and Install Instructions for Fitnesse

That’s the way to do it!

Fitnesse is like Chess.  It takes no time at all to get started but a whole lifetime to master.

I can understand why Drools has such powerful features for developing and deploying with distributed teams and complex production architecture.  It’s the secret sauce being used on mission critical production deployments.  There are teams supporting production who are grateful for this sophistication.

But it comes at a cost.  To realise the true benefits of this new programming paradigm we have to change the way we think.  We cannot do that when we are struggling with the complexity.  To remodel our minds we must be able to play with something fun and engaging.

How can Fitnesse help me to achieve that?

Let me show you.

First we will get Fitnesse and Eclipse working together to provide us with a fast and simple workflow.

I will take you through these steps carefully, so you can get Fitnesse up and running on your machine.

Then we will create the User Guide’s sprinkler example in Eclipse.

Finally I’ll refactor these examples into Fitnesse Fixtures.

I’ll move quickly through these steps to give you an idea of what I’m doing.  If you’re new to Fitnesse don’t expect to follow along.  The refactorings are based on a knowledge of some important Fitnesse concepts that I will explain in later posts.

At the end I’ll share the full implementation so that you should be able to get it up and running.  This will lay the foundation for what is to follow – a deep dive into the Drools core.

This set up isn’t intended for production.  It’s for learning about Drools through play.  It can be useful during the early stages of development so that the team can get into the problem without being distracted.  It is especially useful when you have developers who need to change their way of thinking because it removes the familiar Java IDE.

Getting Fitnesse and Eclipse to Play Nicely Together

Fitnesse goes first

Follow the simple instructions above to get Fitnesse up and running.  If you get an error because the port is already in use then use the -P option to specify another one.  See the cheat sheet for details of the command line options.

When you browse to localhost you’ll see the front page.

Fitnesse Start Page

Fitnesse is a typical Wiki, so the first thing you want to do is create a page to work from.

Just hit Edit and insert this line at the top of the page:

[[Drools Playground][DroolsPlayground.StartHere]]

This will give you a link with question mark.  Click on the question mark to create the new page.

Fitnesse Start Page

You can worry about what you want to write here later.   For now just keep the default text:

!contents -R2 -g -p -f -h

If you want to know what all those options are doing take a look at the User Guide.

Save the page and it will be empty because there are not contents yet.  Lets do something about that!

Hover over the Add button and select the Test Page option.  A new test page will be created.

Fitnesse Start Page

Call the page “HelloWorld”.   It has to be a wiki word with at least two capital letters.  Then copy in the following content.

!|Import|
|uk.co.flowcoding.playground.drools|

|Hello World|
|toString?|
|Hello|
|World|

Hit save and you’ll be returned to the StartHere page showing our new HelloWorld in the table of contents.

Fitnesse Start Page

Follow the HelloWorld link to see the page we created.

Fitnesse Start Page

If you compare the wiki markup with the formatted page you shouldn’t have much difficulty seeing how the two tables are defined.  The question is what are those tables for?  What do they do?  Allow me to explain.

The first table is a package import.

Fitnesse Start Page

It imports Java packages so they can be used in other tables.  These other tables are called fixtures.  Read the full details in the Fitnesse User Guide.

The second table is a fixture.

Fitnesse Start Page

It’s name is mapped to a Java class in the imported package.  The column heading maps to a method.  The question mark tells us it is a method.  Without the question mark it would map to a field.

Hello World will be a Row Fixture.  The User Guide explains:

RowFixture

RowFixture tests dynamic lists of objects. It will compare the expected list (FitNesse table) with the actual result (from fixture code) and report any additional or missing items.

Table Format

The first row of the table is the fixture class name. The second row describes the structure of objects in the list (the properties or methods that you want to verify). All rows after that describe expected objects in the array.

!|RowFixtureTest|
|name|post code|
|John Smith|SW4 66Z|
|Michael Jordan|NE1 8AT|

Hit the test button and see what happens.

Fitnesse Start Page

If fails!

At the top of the page we get a report similar to JUnit informing us that we have 1 exception.

The exception is “Could not find fixture: HelloWorld”.  Notice that the space has been taken out of the table name to turn it into a Java Class name.  Fitnesse has searched it’s classpath for the HelloWorld class and failed with an exception.

To fix this we just need to add our HelloWorld class to the Fitnesse classpath.

Before we can do that we will have to write the HelloWorld class.  For that we will need Eclipse.

Now it’s Eclipse’s turn

I’m assuming you already know Eclipse so I’ll move fast here.

Fitnesse Start Page

  1. Fire up Eclipse and create a new Java Project called Drools Playground.
  2. Add the fitnesse-standalone jar from your Fitnesse installation project to the build path (Using .Build Path – Add External Archives…)
  3. Create a package called uk.co.flowcoding.playground.drools.
  4. Create the following class in that package:
    package uk.co.flowcoding.playground.drools;
    import fit.RowFixture;
    public class HelloWorld extends RowFixture {
        @Override
        public Class getTargetClass() {
            return String.class;
        }
        @Override
        public Object[] query() throws Exception {
            return new String[] {"Hello", "World"};
        }
    }
    

This is a RowFixture.  The Fitnesse User Guide explains:

Fixture class

The fixture class should extend fit.RowFixture and override the following two methods:

getTargetClass — returns the Type or Class object representing the type of objects contained in the array.

query — returns the actual array of objects to be verified.

Now all we need to do is to tell Fitnesse where this code is.

Now Fitnesse and Eclipse Together

We need to add the binary class files to the Fitnesse classpath.

Take a look at the project properties to find out where that is..

First look at the Resource dialogue to see where the project folder is:

Fitnesse Resource

To find out where the binary class files are being built look at the Source tab of the Java Build Path dialogue.

Eclipse Source Properties

On my machine the files can be found in “/home/ged/workspace/DroolsPlayground/bin”.  Your’s may be in a different place.

Fitnesse Start Page

So we set the classpath in Fitnesse.  I set it to “/home/ged/workspace/DroolsPlayground/bin”.  You set it to yours.

To do this just edit the Start Here page and add the following to the to bottom of the page:

!path /home/ged/workspace/DroolsPlayground/bin

Now every page below StartHere will share this class path.

Returning to the HelloWorld test page and hitting Test we get to see the TDDers favourite colour: green.

flow

The Software Flows

You can see how the Row Fixture works.  In the HelloWorld class we can see a string array with two items:

    @Override
    public Object[] query() throws Exception {
        return new String[] {"Hello", "World"};
    }

In the HelloWorld table we see the same two values.

!|Import|
|uk.co.flowcoding.playground.drools|
  
|Hello World|
|toString?|
|Hello|
|World|

Since they both match, everything turns green. Now we alt-tab to Eclipse and change the array:

        return new String[] {"Hello", "Big", "Wide", "World"};

Then we alt-tab to Fitnesse and hit Test again and the green bar has gone. It’s red, with a nice helpful navigator to help us find my failures. flow

Hello and World still match, but now we also have two surplus values: Wide and Big. So we hit edit, add the two values, and test again. Yeah, green bar is back! flow You can see that Row Fixture doesn’t care about the order. If order was important I would have used an Array Fixture.

No plugins to install, no need to mess with Maven. This a nice simple integration based on basic fundamentals.  Fitnesse and Eclipse share a folder and it just works.  I am no longer a slave to my tools – If anything every goes wrong I have a chance of figuring out why and fixing it.  In the process I can only learn more.

Elcipse-Fitnesse-Workflow

If you’re new to Fitnesse I hope this gives you a taste for what it can do. It provides just a few simple features but once you master them there seems no limit to what you can achieve with them.  Uncle Bob shows by example the power of clean and simple can do.

I believe that I will find something just a simple beating at the heart of Drools Expert.

Drools Joins In

with Drools I’m going to introduce the sprinkler example from chapter 6 of the Drools 6.1 documentation.

Just in Eclipse to Begin With

Before tackling the example I follow the User Guide to set up the example.  The following describes the process of trial and error I followed rather than providing a neat set of instructions:

  • I create 4 classes: Room, Sprinkler, Fire and Alarm.
  • I use the Eclipse ‘Encapsulate Field’ re-factoring to create the getters and setters.
  • I create a new folder in the project root called “resources”
  • I create a file in that folder called SprinklerRules.drl.
  • I copy and paste all the example rules from Chapter 6 of the 6.1.0.Final User Guide.
  • At the top of the file I add a package import to match the java classes.
  • I create a new folder under resources called META-INF.
  • I create a file in that folder called kmodule.xml
  • I copy and paste the minimal kModule configuration from the User Guide.
  • In the same package as the 4 classes I create a RunRules class with a public static void main method.
  • I copy the demonstration code from the UserGuide into the main method.
  • Eclipse tells me that this code expects my classes to have constructors, so I add them.

This now gives me the following source code:

In src/…

  • RunRules.java
    • package droolsdocs.chapter6.statefulsession.sprinklers;
      
      import java.util.HashMap;
      import java.util.Map;
      
      import org.kie.api.KieServices;
      import org.kie.api.runtime.KieContainer;
      import org.kie.api.runtime.KieSession;
      import org.kie.api.runtime.rule.FactHandle;
      
      public class RunRules {
      
       public static void main ( String[] args ) {
        //Get a KieSession
        KieServices kieServices = KieServices.Factory.get();
        KieContainer kContainer = kieServices.getKieClasspathContainer();
        KieSession ksession = kContainer.newKieSession();
       
        //Set Up The Rooms and Store them in a Hashmap
        String[] names = new String[]{"kitchen", "bedroom", "office", "living room"};
      
        Map<String,Room> name2room = new HashMap<String,Room>();
      
        for( String name: names ){
         Room room = new Room( name );
         name2room.put( name, room );
         ksession.insert( room );
         Sprinkler sprinkler = new Sprinkler( room );
         ksession.insert( sprinkler );
        }
      
        //Fire the Rules
        ksession.fireAllRules();
       
        //Start Some Fires
        Fire kitchenFire = new Fire( name2room.get( "kitchen" ) );
        Fire officeFire = new Fire( name2room.get( "office" ) );
      
        FactHandle kitchenFireHandle = ksession.insert( kitchenFire );
        FactHandle officeFireHandle = ksession.insert( officeFire );
      
        //Fire the Rules 
        ksession.fireAllRules();
      
        ksession.delete( kitchenFireHandle );
        ksession.delete( officeFireHandle );
      
        ksession.fireAllRules();
       
       }
      }
      
  • Room.java
    • package droolsdocs.chapter6.statefulsession.sprinklers;
      
      public class Room {
       private String name;
      
       public Room(String name) {
       setName(name);
       }
      
       public String getName() {
       return name;
       }
      
       public void setName(String name) {
       this.name = name;
       }
      }
      
  • Sprinkler.java
    • package droolsdocs.chapter6.statefulsession.sprinklers;
      
      public class Sprinkler {
       private Room room;
       private boolean on;
       
       public Sprinkler(Room room) {
       setRoom(room);
       }
       public Room getRoom() {
       return room;
       }
       public void setRoom(Room room) {
       this.room = room;
       }
       public boolean isOn() {
       return on;
       }
       public void setOn(boolean on) {
       this.on = on;
       }
      }
      
  • Fire.java
    • package droolsdocs.chapter6.statefulsession.sprinklers;
      
      public class Fire {
       private Room room;
      
       public Fire(Room room) {
       setRoom(room);
       }
      
       public Room getRoom() {
       return room;
       }
      
       public void setRoom(Room room) {
       this.room = room;
       }
      }
      
  • Alarm.java
    • package droolsdocs.chapter6.statefulsession.sprinklers;
      
      public class Alarm {
       private String name;
      
       public String getName() {
       return name;
       }
      
       public void setName(String name) {
       this.name = name;
       }
      }
      

in resources/

  • SprinklerRules.drl
    • package droolsdocs.chapter6.statefulsession.sprinklers;
      
      rule "When there is a fire turn on the sprinkler"
      when
       Fire($room : room)
       $sprinkler : Sprinkler( room == $room, on == false )
      then
       modify( $sprinkler ) { setOn( true ) };
       System.out.println( "Turn on the sprinkler for room " + $room.getName() );
      end
      
      rule "When the fire is gone turn off the sprinkler";
      when
       $room : Room( )
       $sprinkler : Sprinkler( room == $room, on == true )
       not Fire( room == $room )
      then
       modify( $sprinkler ) { setOn( false ) };
       System.out.println( "Turn off the sprinkler for room " + $room.getName() );
      end
      
      rule "Raise the alarm when we have one or more fires"
      when
       exists Fire()
      then
       insert( new Alarm() );
       System.out.println( "Raise the alarm" );
      end
      
      rule "Cancel the alarm when all the fires have gone"
      when
       not Fire()
       $alarm : Alarm()
      then
       delete( $alarm );
       System.out.println( "Cancel the alarm" );
      end
      
      rule "Status output when things are ok"
      when
       not Alarm()
       not Sprinkler( on == true ) 
      then
       System.out.println( "Everything is ok" );
      end
      
  • META-INF/kmodule.xml
    • <?xml version="1.0" encoding="UTF-8" ?>
      <kmodule xmlns="http://jboss.org/kie/6.0.0/kmodule"/>
      

Before the code will run I need to provide the JARs that the code depends upon.  I discover these trough trial and error.

  • I create a lib folder alongside src and resources.
  • I download Drools 6.1 distribution.
  • I copy folders from the distribution bin into my project’s lib folder and add them to the Eclipse build path.

Within the Drools 6.1 distribution there is a bin folder full of Jars.  I use FindJars to find out what Jars I need.  First I add what is needed to import KieServices, KieContainer and KieSession.  Then I run the code and find the jars needed to resolve the class not found exceptions.

Here is my final list of Jars:

Still more than I would like.  I shall come back to these in a later post.  For now, however, I press on.

With code and Jars in place I can run the code and I’m rewarded with the expected result:

  • SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
    SLF4J: Defaulting to no-operation (NOP) logger implementation
    SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.
    Everything is ok
    Turn on the sprinkler for room kitchen
    Turn on the sprinkler for room office
    Raise the alarm
    Turn off the sprinkler for room kitchen
    Turn off the sprinkler for room office
    Cancel the alarm
    Everything is ok
    

Bringing in Fitnesse

Now to take this code an translate it into a set of Fitnesse Fixtures.

Creating Fitnesse Fixtures should be treated as the Creation of a Domain Specific Language.  We want to break our code down into the high level tasks being completed, and give each of those a Fixture.   Then we can mix and match those fixtures to create new scenarios without having to write code.

My goal is to make the Fitnesse DSL as expressive as possible, allowing me to implement all the scenarios I’m interested in without having to write any scenario specific code.  To reach that goal I need to take one step at a time, so I start with Run Rules.  I wan’t to eliminate that method all together, since it is full of scenario specific code.

So let’s break the RunRules class up using the basic Extract Method refactoring.

Doing this gives me the following

package droolsdocs.chapter6.statefulsession.sprinklers;

import java.util.HashMap;
import java.util.Map;

import org.kie.api.KieServices;
import org.kie.api.runtime.KieContainer;
import org.kie.api.runtime.KieSession;
import org.kie.api.runtime.rule.FactHandle;

public class RunRules {

 private static KieSession ksession;
 
 private static Map<String,Room> name2room = new HashMap<String,Room>(); 
 
 private static void setUpKieSession() {
  KieServices kieServices = KieServices.Factory.get();
  KieContainer kContainer = kieServices.getKieClasspathContainer();
  ksession = kContainer.newKieSession();
 }
 
 private static void AddRooms(String... names) {
  for( String name: names ){
   Room room = new Room( name );
   name2room.put( name, room );
   ksession.insert( room );
   Sprinkler sprinkler = new Sprinkler( room );
   ksession.insert( sprinkler );
  }
 }
 
 private static void fireTheRules() {
  ksession.fireAllRules();
 }
 
 private static FactHandle startFireInRoom(String name) {
  Fire fire = new Fire( name2room.get( name ) );
  return ksession.insert( fire );
 }
 
 private static void stopFire(FactHandle fire) {
  ksession.delete(fire);
 }
 
 public static void main ( String[] args ) {
 
 setUpKieSession();
  AddRooms("kitchen", "bedroom", "office", "livingroom");
  fireTheRules();
  FactHandle kitchenFireHandle = startFireInRoom("kitchen");
  FactHandle officeFireHandle = startFireInRoom("office");
  fireTheRules();
  stopFire(kitchenFireHandle);
  stopFire(officeFireHandle);
  fireTheRules();
 }
}

There are a couiple of awkward features I want to smooth out here:

  • The Room2Names map
  • The FactHandles required to stop a fire.

To tackle these I refactor some more.

First of all I get rid of the Rome2Names map by making the FactHandle the only object reference I pass around.  I use the getObject(FactHandle) method to retrieve the Room objects.

Then I replace all the FactHandles with their External forms and use a DisconnectedFactHandle.  Now all I pass around are strings.

Now I am ready to move the code to Fixtures.

Playtime!

I can now share with you the files for the working Drools playground.  Over the next couple of weeks I’ll share my approach with Fitnesse as well as my discovery of the Drools core in a serious of blog posts.  You’ll be glad to hear that those blog posts will be much shorter.

I’m currently setting up the project on GitHub.  I’ll update this post with the details when it’s available.