Picture the scene: a self help group meeting, plastic chairs arranged in a circle. Sitting on the chairs are an assortment of (mainly) men in their 20’s or 30’s, some smartly dressed, others with 2 day old beards. They fail to look each other in the eye, until one plucks up the courage and mumbles ‘Hello, I’m Paul , and I’ve been writing bad Java code for 10 years‘.
Taking a deep breath he continues: ‘When I got into Java I was using JSP for everything - HTML, talking to databases, doing workflow - anything I could get my hands on‘. Seeing the shock on his comrades faces he adds ‘I was young and I didn’t know what I was doing‘. ‘Then I found Struts and MVC and things became a little bit better. My days had a bit more structure, but things didn’t seem right. Even after I got treatment based on EJB, Spring and Hibernate, I still feel that there is a void at the centre of my coding life‘.
The rest of the group nodded - This was a classic case. ‘I fell in with a bad crowd. Business types with suits and violin cases. They said they’d pay me good money if I built them something just like they asked. Now they don’t believe that it works - it’s all techie stuff to them. Those boys are going to play rough if I can’t show them the goods and let them review the code. What can I do?‘
There was silence for a while. Then the group leader said
You need to build a system where non-technical people can review the important parts. You’ve used all the major frameworks, and while they’re good in what they do, they’re not helping here. And if you sacrifice maintainability , performance or speed of development, the suits are going to get you.
‘It’s a tough one. Does anybody have any suggestions?’
What would YOU suggest?
One suggestion is to use Flow diagrams (the things that look a bit like Visio further down the page) and Business Rules (simple if.. then statements). Think your boss and business customers (aka the guy with the suit) could understand these? Yes? Now the only problem now is how to use it to replace your ‘bad’ (unreadable to business users) Java code.
JBoss Drools (on which the sample below is based) gives you both flow diagrams and business rules and a web based interface to allow the suits to review the rules you have written. It allows meaning that would normally be hidden in Java code to be reviewed. It may be beautifully written Java but Java code means nothing to most business users. Using JBoss Drools, if anybody ever says ‘you missed out the rule that says not to payout if the claim is greater than 100k’, you can reply ‘but you reviewed the rules and passed them as ok!’.
To prove the point, look at the images below. This sample is based on one from the JBoss Drools Project. It’s a simple ‘guess a number between 1 and 100′ program, with 5 chances to get it right. What it does not show is the scalability of a Rules based project where you might have hundreds if not thousands of rules in your system. It does explain how to use rules in your project, and some of the features that might make your code clearer.
The actual sample code (including an Eclipse project to get you started quickly) can be found at the JBoss Download site - pick the ‘Drools Examples’ option. Within these samples, there is also a ‘Hello World’ example for a really simple kickstart. You may also want the Drools Plugins for Eclipse - use the Drools IDE Update Site url within the Eclipse update manager in the normal way.
Going through the Number Guess Sample
If you have downloaded the samples, you will see that the “Number Guess” example shows the use of Rules and RuleFlow, a way of controlling when rules are fired. It uses workflow diagrams to make clear the order that
rules will be executed. Our NumberGuess.java has a standard main() method, which is where our program starts when we run it (either via Eclipse , or the command line)
Loading the Business Rules - NumberGuessExample.java main() method
final PackageBuilder builder = new PackageBuilder(); builder.addPackageFromDrl( new InputStreamReader( ShoppingExample.class.getResourceAsStream( "NumberGuess.drl" ) ) ); builder.addRuleFlow( new InputStreamReader( ShoppingExample.class.getResourceAsStream( "NumberGuess.rfm" ) ) ); final RuleBase ruleBase = RuleBaseFactory.newRuleBase(); ruleBase.addPackage( builder.getPackage() );
The code extract shows us finding and loading the rules file from the classpath (using
the addPackageFromDrl() method ). A 2nd line finds and loads the RuleFlow (NumberGuess.rfm) file. Once we have our rules and flow in memory , we can start to tell it about our known facts.
RuleFlow - extract 2 from NumberGuessExample.java main() method
final StatefulSession session = ruleBase.newStatefulSession(); session.insert( new GameRules( 100, 5 ) ); session.insert( new RandomNumber() ); session.insert( new Game() ); session.startProcess( "Number Guess" ); session.fireAllRules(); session.dispose();
Once we have a RuleBase we can use it to obtain a session - similar in concept to a Web Server session, only for dealing with our Rules. Into this session we insert our facts (simple Java Objects) , things that we know to be true. For simplicity in this sample, these classes are all contained within
our NumberGuessExample.java file.
The three simple Java classes are as follows:
- The GameRules class provides the maximum range and the number of guesses allowed.
- The RandomNumber class automatically generates a number between 0 and 100 and makes it
available to our rules after insertion (via the getValue() method).
- The Game class keeps track of the guesses we have made before, and the
number of guesses we have made.
Note that before we call the standard fireAllRules() method, we
also start the Rule Flow that we loaded earlier (via the startProcess()
method). We explain where to obtain the parameter we pass (”Number
Guess” - the id of the ruleflow) when we talk about the RuleFlow file
and the graphical RuleFlow editor below.
Before we finish we our Java code , we note that In ‘real life’
we would examine the final state of the objects after our rules had fired. For example, we might note how many guesses the user needed , so that we could add it to a high score table. For this
example we are content to ensure the working memory session is cleared
by calling the dispose() method.
Once fireAllRules() is called, the program flow of control goes to the RuleFlow, as per the diagram below.
RuleFlow for the
NumberGuess Example - right click and ‘view image’ to see it full size
If you open the NumberGuess.rf (warning - easier to view in the graphical editor!) file open in the Drools IDE (or
have the JBoss Drools extensions installed correctly in Eclipse) you
should see the above diagram, similar to a standard flowchart. Its icons
are similar (but not exactly the same) as the JBoss jBPM workflow
product. Should you wish to edit the diagram, a menu of available
components should be available in the Palette the left of the diagram in the IDE. This diagram is saved in a (almost human) readable xml format.
To give an overview of each of the node types (boxes) in the
The Start and End nodes (green arrow and red box) are where the
RuleFlow starts and ends.
RuleFlowGroup (simple yellow box). These map to the
RuleFlowGroups in our rules (DRL) file that we will look at later. For
example when the flow reaches the ‘Too High’ RuleFlowGroup, only those
rules marked with an attribute of ruleflow-group
“Too High” can potentially fire.
Action Nodes (yellow box with cog like icon). These can perform
standard Java method calls. Most action nodes in this example call
System.out.println to give an indication to the user of what is going
Split and Join Nodes (Blue Ovals) such as “Guess Correct” and
“More Guesses Join” where the flow of control can split (according to
various conditions) and / or rejoin.
- Arrows that indicate the flow between the various nodes.
These various nodes work together with the Rules contained in the NumberGuess.drl to make the
Number Guess game work. For example, the “Guess” RuleFlowGroup allows
only the first rule, “Get user Guess”, to fire (details below) as only that Rule
matches the ruleflow-group
In total, there are five (easy to read rules) in our example
- A rule to display a message to the user, then wait for a guess.
- A Rule to record the highest guess.
- A Rule to record the lowest guess.
A Rule to inspect the guess and retract it from memory if
A Rule that notifies the user that all guesses have been used
The Split Nodes (the blue ovals in the diagram above) can use values in working memory
(as updated by the Rules) to decide which flow of action to take. To see
how this works click on the “Guess Correct Node” ; then within the
properties view, open the constraints editor (the button at the right
that appears once you click on the ‘Constraints’ property line). You
should see something similar to the Diagram below.
Edit Constraints for
the GuessCorrect Node
Click on ‘Edit’ beside ‘To node Too High’ and you see a dialog
like the one below. The values in the ‘Textual Editor’ follow the
standard Rule Format (LHS) and can refer to objects in working memory.
The consequence (RHS) is that the flow of control follows this node
(i.e. To node Too high’) if the LHS expression evaluates to true.
for the GuessCorrect Node / value too high
Since the NumberGuess.java example contains a main() method, it
can be run as a standard Java application (either from the command line
or via the IDE). A typical game might result in the interaction below
(the numbers in bold are typed in by the user).
output where the Number Guess Example beat the human!
You have 5 out of 5 guesses left. Please enter your guess from 0 to 100 50 Your guess was too high You have 4 out of 5 guesses left. Please enter your guess from 0 to 100 25 Your guess was too low You have 3 out of 5 guesses left. Please enter your guess from 0 to 100 37 Your guess was too low You have 2 out of 5 guesses left. Please enter your guess from 0 to 100 44 Your guess was too low You have 1 out of 5 guesses left. Please enter your guess from 0 to 100 47 Your guess was too low You have no more guesses The correct guess was 48
What happens in this sample
A summary of what is happening in this sample when you run it is:
The Main() method of NumberGuessExample.java loads RuleBase, gets a
StatefulSession and inserts Game, GameRules and RandomNumber
(containing the target number) objects into it. This method sets the
process flow we are going to use, and fires all rules. Control passes
to the RuleFlow.
- The NumberGuess.rf RuleFlow begins at the Start node.
Control passes (via the “more guesses” join node) to the Guess
At the Guess node, the appropriate RuleFlowGroup (”Get user
Guess”) is enabled. In this case the Rule “Guess” (in the
NumberGuess.drl file) is triggered. This rule displays a message to the
user, takes the response, and puts it into memory. Flow passes to the
next Rule Flow Node.
At the next node , “Guess Correct”, constraints inspect the
current session and decide which path we take next.
If the guess in step 4 was too high / too low flow procees along
a path which has (i) An action node with normal Java code prints a too
high / too low statement and (ii) a RuleFlowGroup causes a highest
guess / lowest guess Rule to be triggered in the Rules file. Flow
passes from these nodes to step 6.
If the guess in step 4 just right we proceed along the path
towards the end of the Rule Flow. Before we get there, an action node
with normal Java code prints a statement “you guessed correctly”. There
is a join node here (just before the Rule Flow End) so that our
no-more-guesses path (step 7) can also terminate the RuleFlow.
Control passes as per the RuleFlow via a join node, a guess
incorrect RuleFlowGroup (triggers a rule to retract a guess from
working memory) onto the “more guesses” decision node.
The “more guesses” decision node (right hand side of ruleflow)
uses constraints (again looking at values that the Rules have put into
the working memory) to decide if we have more guesses and if so, goto
step 3. If not we proceed to the end of the workflow, via a
RuleFlowGroup that triggers a rule stating “you have no more guesses”.
The Loop 3-7 continues until the number is guessed correctly, or
we run out of guesses.
The (small) bit of the Rules documentation that this article is based on was contributed by the Author (you can see there entire JBoss Drools documentation at this URL). If you want more information go to the Rules user and dev lists on the JBoss Drools Project Homepage. They’re nice guys, but are in no way responsible for the opinions voiced in this article. If you agree/disagree with this blogpost, please leave a comment using the textbox below.