Hello World

Name: Hello World
Main class: org.drools.examples.HelloWorldExample
Type: Java application
Rules file: HelloWorld.drl
Objective: demonstrate basic rules in use

The "Hello World" example shows a simple example of rules usage, and both the MVEL and Java dialects.

This example demonstrates how to build Knowledge Bases and Sessions. Also, audit logging and debug outputs are shown, which is ommitted from other examples as it's all very similar. A KnowledgeBuilder is used to turn a DRL source file into Package objects which the Knowledge Base can consume. The add method takes a Resource interface and a Resource Type as parameters. The Resource can be used to retrieve a DRL source file from various locations; in this case the DRL file is being retrieved from the classpath using a ResourceFactory, but it could come from a disk file or a URL. Here, we only add a single DRL source file, but multiple DRL files can be added. Also, DRL files with different namespaces can be added, where the Knowledge Builder creates a package for each namespace. Multiple packages of different namespaces can be added to the same Knowledge Base. When all the DRL files have been added, we should check the builder for errors. While the Knowledge Base will validate the package, it will only have access to the error information as a String, so if you wish to debug the error information you should do it on the KnowledgeBuilder instance. Once you know the builder is error free, get the Package collection, instantiate a KnowledgeBase from the KnowledgeBaseFactory and add the package collection.

Example 8.1. HelloWorld: Creating the KnowledgeBase and Session

final KnowledgeBuilder kbuilder = KnowledgeBuilderFactory.newKnowledgeBuilder();

// this will parse and compile in one step
kbuilder.add(ResourceFactory.newClassPathResource("HelloWorld.drl",
				HelloWorldExample.class), ResourceType.DRL);

// Check the builder for errors
if (kbuilder.hasErrors()) {
    System.out.println(kbuilder.getErrors().toString());
    throw new RuntimeException("Unable to compile \"HelloWorld.drl\".");
}

// get the compiled packages (which are serializable)
final Collection<KnowledgePackage> pkgs = kbuilder.getKnowledgePackages();

// add the packages to a knowledgebase (deploy the knowledge packages).
final KnowledgeBase kbase = KnowledgeBaseFactory.newKnowledgeBase();
kbase.addKnowledgePackages(pkgs);

final StatefulKnowledgeSession ksession = kbase.newStatefulKnowledgeSession();

Drools has an event model that exposes much of what's happening internally. Two default debug listeners are supplied, DebugAgendaEventListener and DebugWorkingMemoryEventListener which print out debug event information to the System.err stream displayed in the Console window. Adding listeners to a Session is trivial, as shown below. The KnowledgeRuntimeLogger provides execution auditing, the result of which can be viewed in a graphical viewer. The logger is actually a specialised implementation built on the Agenda and Working Memory listeners. When the engine has finished executing, logger.close() must be called.

Most of the examples use the Audit logging features of Drools to record execution flow for later inspection.

Example 8.2. HelloWorld: Event logging and Auditing

// setup the debug listeners
ksession.addEventListener( new DebugAgendaEventListener() );
ksession.addEventListener( new DebugWorkingMemoryEventListener() );
        
// setup the audit logging
KnowledgeRuntimeLogger logger =
  KnowledgeRuntimeLoggerFactory.newFileLogger(ksession, "log/helloworld");

The single class used in this example is very simple. It has two fields: the message, which is a String and the status which can be one of the two integers HELLO or GOODBYE.

Example 8.3. HelloWorld example: Message Class

public static class Message {
    public static final int HELLO   = 0;
    public static final int GOODBYE = 1;

    private String          message;
    private int             status; 
    ...
}

A single Message object is created with the message text "Hello World" and the status HELLO and then inserted into the engine, at which point fireAllRules() is executed. Remember that all the network evaluation is done during the insert time, so that by the time the program execution reaches the fireAllRules() method call the engine already knows which rules are fully matches and able to fire.

Example 8.4. HelloWorld: Execution

final Message message = new Message();
message.setMessage("Hello World");
message.setStatus(Message.HELLO);
ksession.insert(message);

ksession.fireAllRules();

logger.close();

ksession.dispose();    

To execute the example as a Java application:

  1. Open the class org.drools.examples.HelloWorldExample in your Eclipse IDE

  2. Right-click the class and select "Run as..." and then "Java application"

If we put a breakpoint on the fireAllRules() method and select the ksession variable, we can see that the "Hello World" rule is already activated and on the Agenda, confirming that all the pattern matching work was already done during the insert.

Figure 8.1. Hello World: fireAllRules Agenda View

Hello World: fireAllRules Agenda View

The may application print outs go to to System.out while the debug listener print outs go to System.err.

Example 8.5. HelloWorld: System.out in the Console window

Hello World
Goodbye cruel world

Example 8.6. HelloWorld: System.err in the Console window

==>[ActivationCreated(0): rule=Hello World; 
                   tuple=[fid:1:1:org.drools.examples.HelloWorldExample$Message@17cec96]]
[ObjectInserted: handle=[fid:1:1:org.drools.examples.HelloWorldExample$Message@17cec96]; 
                 object=org.drools.examples.HelloWorldExample$Message@17cec96]
[BeforeActivationFired: rule=Hello World; 
                   tuple=[fid:1:1:org.drools.examples.HelloWorldExample$Message@17cec96]]
==>[ActivationCreated(4): rule=Good Bye; 
                   tuple=[fid:1:2:org.drools.examples.HelloWorldExample$Message@17cec96]]
[ObjectUpdated: handle=[fid:1:2:org.drools.examples.HelloWorldExample$Message@17cec96]; 
                old_object=org.drools.examples.HelloWorldExample$Message@17cec96; 
                new_object=org.drools.examples.HelloWorldExample$Message@17cec96]
[AfterActivationFired(0): rule=Hello World]
[BeforeActivationFired: rule=Good Bye; 
                   tuple=[fid:1:2:org.drools.examples.HelloWorldExample$Message@17cec96]]
[AfterActivationFired(4): rule=Good Bye]  

The LHS (after <kw>when</kw>) section of the rule states that it will be activated for each Message object inserted into the Working Memory whose status is Message.HELLO. Besides that, two variable bindings are created: the variable message is bound to the message attribute and the variable m is bound to the matched Message object itself.

The RHS (after <kw>then</kw>) or consequence part of the rule is written using the MVEL expression language, as declared by the rule's attribute dialect. After printing the content of the bound variable message to System.out, the rule changes the values of the message and status attributes of the Message object bound to m. This is done MVEL's <kw>modify</kw> statement, which allows you to apply a block of assignments in one statement, with the engine being automatically notified of the changes at the end of the block.

Example 8.7. HelloWorld: rule "Hello World"

rule "Hello World"
      dialect "mvel"
  when
      m : Message( status == Message.HELLO, message : message )
  then
      System.out.println( message ); 
      modify ( m ) { message = "Goodbyte cruel world",
                     status = Message.GOODBYE };
end

We can set a breakpoint into the DRL, on the <kw>modify</kw> call, and inspect the Agenda view again during the execution of the rule's consequence. This time we start the execution via "Debug As" and "Drools application" and not by running a "Java application":

  1. Open the class org.drools.examples.HelloWorld in your Eclipse IDE.

  2. Right-click the class and select "Debug as..." and then "Drools application".

Now we can see that the other rule "Good Bye", which uses the Java dialect, is activated and placed on the Agenda.

Figure 8.2. Hello World: rule "Hello World" Agenda View

Hello World: rule "Hello World" Agenda View

The "Good Bye" rule, which specifies the "java" dialect, is similar to the "Hello World" rule except that it matches Message objects whose status is Message.GOODBYE.

Example 8.8. HelloWorld: rule "Good Bye"

rule "Good Bye"
      dialect "java"
  when
      Message( status == Message.GOODBYE, message : message )
  then
      System.out.println( message ); 
end

Remember the Java code where we used the KnowledgeRuntimeLoggerFactory method newFileLogger to create a KnowledgeRuntimeLogger and called logger.close() at the end. This created an audit log file that can be shown in the Audit view. We use the Audit view in many of the examples to demostrate the example execution flow. In the view screen shot below we can see that the object is inserted, which creates an activation for the "Hello World" rule; the activation is then executed which updates the Message object causing the "Good Bye" rule to activate; finally the "Good Bye" rule also executes. Selecting an event in the Audit view highlights the origin event in green; therefore the "Activation created" event is highlighted in green as the origin of the "Activation executed" event.

Figure 8.3. Hello World: Audit View

Hello World: Audit View