JBoss.orgCommunity Documentation

Drools Introduction and General User Guide

Version 5.4.0.CR1


1. Welcome
2. Installation and Setup (Core and IDE)
2.1. Installing and using
2.1.1. Dependencies and jars
2.1.2. Runtime
2.1.3. Installing IDE (Rule Workbench)
2.2. Building from source
2.2.1. Getting the sources
2.2.2. Building the sources
2.3. Eclipse
2.3.1. Importing Eclipse Projects
3. Getting Involved
3.1. Sign up to jboss.org
3.2. Sign the Contributor Agreement
3.3. Submitting issues via JIRA
3.4. Fork Github
3.5. Writing Tests
3.6. Commit with Correct Conventions
3.7. Submit Pull Requests
4. Drools Release Notes
4.1. What is New and Noteworthy in Drools 5.4.0.CR1
4.1.1. Drools Expert
4.1.2. Guvnor
4.1.3. Planner
4.2. What is New and Noteworthy in Drools 5.4.0.Beta2
4.2.1. Drools Expert
4.2.2. Drools Fusion
4.2.3. Guvnor
4.2.4. Planner
4.3. What is New and Noteworthy in Drools 5.4.0.Beta1
4.3.1. Drools Expert
4.3.2. Guvnor
4.3.3. Planner
4.3.4. Eclipse plugin
4.4. What is New and Noteworthy in Drools 5.3.0
4.4.1. Drools Expert
4.4.2. Guvnor
4.4.3. Drools Planner
4.4.4. Drools Integration
4.5. What is New and Noteworthy in Drools 5.2.0
4.5.1. Knowledge API (renamed from Drools API)
4.5.2. Drools Expert and Fusion
4.5.3. Drools and jBPM integration
4.5.4. Merging Drools Flow into jBPM5
4.5.5. Guvnor
4.5.6. Eclipse
4.5.7. Maven artifactId changes
4.6. What is New and Noteworthy in Drools 5.1.0
4.6.1. Drools API
4.6.2. Core
4.6.3. Expert
4.6.4. Flow
4.6.5. Guvnor
4.6.6. Eclipse
4.6.7. Known Issues
4.7. What is New and Noteworthy in Drools 5.0.0
4.7.1. Drools API
4.7.2. Drools Guvnor
4.7.3. Drools Expert
4.7.4. Drools Flow
4.7.5. Drools Fusion
4.7.6. Eclipse IDE
4.8. What is new in Drools 4.0
4.8.1. Language Expressiveness Enhancements
4.8.2. Core Engine Enhancements
4.8.3. IDE Enhancements
4.8.4. Business Rules Management System - BRMS
4.8.5. Miscellaneous Enhancements
4.9. Upgrade tips from Drools 3.0.x to Drools 4.0.x
4.9.1. API changes
4.9.2. Rule Language Changes
4.9.3. Drools Update Tool
4.9.4. DSL Grammars in Drools 4.0
4.9.5. Rule flow Update for 4.0.2
5. Drools compatibility matrix

I've always stated that end business users struggle understanding the differences between rules and processes, and more recently rules and event processing. For them they have this problem in their mind and they just want to model it using some software. The traditional way of using two vendor offerings forces the business user to work with a process oriented or rules oriented approach which just gets in the way, often with great confusion over which tool they should be using to model which bit.

PegaSystems and Microsoft have done a great job of showing that the two can be combined and a behavioural modelling approach can be used. This allows the business user to work more naturally where the full range of approaches is available to them, without the tools getting in the way. From being process oriented to rule oriented or shades of grey in the middle - whatever suites the problem being modelled at that time.

Drools 5.0 takes this one step further by not only adding BPMN2 based workflow with Drools Flow but also adding event processing with Drools Fusion, creating a more holistic approach to software development. Where the term holistic is used for emphasizing the importance of the whole and the interdependence of its parts.

Drools 5.0 is now split into 5 modules, each with their own manual - Guvnor (BRMS/BPMS), Expert (Rules), Fusion (CEP), Flow (Process/Workflow) and Planner. Guvnor is our web based governance system, traditionally referred to in the rules world as a BRMS. We decided to move away from the BRMS term to a play on governance as it's not rules specific. Expert is the traditional rules engine. Fusion is the event processing side, it's a play on data/sensor fusion terminology. Flow is our workflow module, Kris Verlaenen leads this and has done some amazing work; he's currently moving flow to be incorporated into jBPM 5. The fith module called Planner, authored by Geoffrey De Smet, solves allocation and scheduling type problem and while still in the early stage of development is showing a lot of promise. We hope to add Semantics for 2011, based around description logc, and that is being work on as part of the next generaion Drools designs.

I've been working in the rules field now for around 7 years and I finally feel like I'm getting to grips with things and ideas are starting to gel and the real innovation is starting to happen. To me It feels like we actually know what we are doing now, compared to the past where there was a lot of wild guessing and exploration. I've been working hard on the next generation Drools Expert design document with Edson Tirelli and Davide Sottara. I invite you to read the document and get involved, http://community.jboss.org/wiki/DroolsLanguageEnhancements. The document takes things to the next level pushing Drools forward as a hybrid engine, not just a capable production rule system, but also melding in logic programming (prolog) with functional programming and description logic along with a host of other ideas for a more expressive and modern feeling language.

I hope you can feel the passion that my team and I have while working on Drools, and that some of it rubs off on you during your adventures.

Drools provides an Eclipse-based IDE (which is optional), but at its core only Java 1.5 (Java SE) is required.

A simple way to get started is to download and install the Eclipse plug-in - this will also require the Eclipse GEF framework to be installed (see below, if you don't have it installed already). This will provide you with all the dependencies you need to get going: you can simply create a new rule project and everything will be done for you. Refer to the chapter on the Rule Workbench and IDE for detailed instructions on this. Installing the Eclipse plug-in is generally as simple as unzipping a file into your Eclipse plug-in directory.

Use of the Eclipse plug-in is not required. Rule files are just textual input (or spreadsheets as the case may be) and the IDE (also known as the Rule Workbench) is just a convenience. People have integrated the rule engine in many ways, there is no "one size fits all".

Alternatively, you can download the binary distribution, and include the relevant jars in your projects classpath.

Drools is broken down into a few modules, some are required during rule development/compiling, and some are required at runtime. In many cases, people will simply want to include all the dependencies at runtime, and this is fine. It allows you to have the most flexibility. However, some may prefer to have their "runtime" stripped down to the bare minimum, as they will be deploying rules in binary form - this is also possible. The core runtime engine can be quite compact, and only requires a few 100 kilobytes across 3 jar files.

The following is a description of the important libraries that make up JBoss Drools

There are quite a few other dependencies which the above components require, most of which are for the drools-compiler, drools-jsr94 or drools-decisiontables module. Some key ones to note are "POI" which provides the spreadsheet parsing ability, and "antlr" which provides the parsing for the rule language itself.

NOTE: if you are using Drools in J2EE or servlet containers and you come across classpath issues with "JDT", then you can switch to the janino compiler. Set the system property "drools.compiler": For example: -Ddrools.compiler=JANINO.

For up to date info on dependencies in a release, consult the released poms, which can be found on the maven repository.

The rule workbench (for Eclipse) requires that you have Eclipse 3.4 or greater, as well as Eclipse GEF 3.4 or greater. You can install it either by downloading the plug-in or, or using the update site.

Another option is to use the JBoss IDE, which comes with all the plug-in requirements pre packaged, as well as a choice of other tools separate to rules. You can choose just to install rules from the "bundle" that JBoss IDE ships with.

Download the Drools Eclipse IDE plugin from the link below. Unzip the downloaded file in your main eclipse folder (do not just copy the file there, extract it so that the feature and plugin jars end up in the features and plugin directory of eclipse) and (re)start Eclipse.

http://www.jboss.org/drools/downloads.html

To check that the installation was successful, try opening the Drools perspective: Click the 'Open Perspective' button in the top right corner of your Eclipse window, select 'Other...' and pick the Drools perspective. If you cannot find the Drools perspective as one of the possible perspectives, the installation probably was unsuccessful. Check whether you executed each of the required steps correctly: Do you have the right version of Eclipse (3.4.x)? Do you have Eclipse GEF installed (check whether the org.eclipse.gef_3.4.*.jar exists in the plugins directory in your eclipse root folder)? Did you extract the Drools Eclipse plugin correctly (check whether the org.drools.eclipse_*.jar exists in the plugins directory in your eclipse root folder)? If you cannot find the problem, try contacting us (e.g. on irc or on the user mailing list), more info can be found no our homepage here:

http://www.jboss.org/drools/

A Drools runtime is a collection of jars on your file system that represent one specific release of the Drools project jars. To create a runtime, you must point the IDE to the release of your choice. If you want to create a new runtime based on the latest Drools project jars included in the plugin itself, you can also easily do that. You are required to specify a default Drools runtime for your Eclipse workspace, but each individual project can override the default and select the appropriate runtime for that project specifically.

You are required to define one or more Drools runtimes using the Eclipse preferences view. To open up your preferences, in the menu Window select the Preferences menu item. A new preferences dialog should show all your preferences. On the left side of this dialog, under the Drools category, select "Installed Drools runtimes". The panel on the right should then show the currently defined Drools runtimes. If you have not yet defined any runtimes, it should like something like the figure below.

To define a new Drools runtime, click on the add button. A dialog as shown below should pop up, requiring the name for your runtime and the location on your file system where it can be found.

In general, you have two options:

After clicking the OK button, the runtime should show up in your table of installed Drools runtimes, as shown below. Click on checkbox in front of the newly created runtime to make it the default Drools runtime. The default Drools runtime will be used as the runtime of all your Drools project that have not selected a project-specific runtime.

You can add as many Drools runtimes as you need. For example, the screenshot below shows a configuration where three runtimes have been defined: a Drools 4.0.7 runtime, a Drools 5.0.0 runtime and a Drools 5.0.0.SNAPSHOT runtime. The Drools 5.0.0 runtime is selected as the default one.

Note that you will need to restart Eclipse if you changed the default runtime and you want to make sure that all the projects that are using the default runtime update their classpath accordingly.

Whenever you create a Drools project (using the New Drools Project wizard or by converting an existing Java project to a Drools project using the "Convert to Drools Project" action that is shown when you are in the Drools perspective and you right-click an existing Java project), the plugin will automatically add all the required jars to the classpath of your project.

When creating a new Drools project, the plugin will automatically use the default Drools runtime for that project, unless you specify a project-specific one. You can do this in the final step of the New Drools Project wizard, as shown below, by deselecting the "Use default Drools runtime" checkbox and selecting the appropriate runtime in the drop-down box. If you click the "Configure workspace settings ..." link, the workspace preferences showing the currently installed Drools runtimes will be opened, so you can add new runtimes there.

You can change the runtime of a Drools project at any time by opening the project properties (right-click the project and select Properties) and selecting the Drools category, as shown below. Check the "Enable project specific settings" checkbox and select the appropriate runtime from the drop-down box. If you click the "Configure workspace settings ..." link, the workspace preferences showing the currently installed Drools runtimes will be opened, so you can add new runtimes there. If you deselect the "Enable project specific settings" checkbox, it will use the default runtime as defined in your global preferences.

We are often asked "How do I get involved". Luckily the answer is simple, just write some code and submit it :) There are no hoops you have to jump through or secret handshakes. We have a very minimal "overhead" that we do request to allow for scalable project development. Below we provide a general overview of the tools and "workflow" we request, along with some general advice.

If you contribute some good work, don't forget to blog about it :)

In Drools when you invoke update() or modify() on a given object it will trigger a revaluation of all patterns of the matching object type in the knowledge base. As some have experienced, this can be a problem that often can lead to unwanted and useless evaluations and in the worst cases to infinite recursions. The only workaround to avoid it was to split up your objects into smaller ones having a 1 to 1 relationship with the original object.

This new feature allows the pattern matching to only react to modification of properties actually constrained or bound inside of a given pattern. That will help with performance and recursion and avoid artificial object splitting.

By default this feature is off in order to make the behavior of the rule engine backward compatible with the former releases. When you want to activate it on a specific bean you have to annotate it with @propertyReactive. This annotation works both on drl type declarations:

declare Person
    @propertyReactive
    firstName : String
    lastName : String
end

and on Java classes:

@PropertyReactive
public static class Person {
    private String firstName;
    private String lastName;
}

In this way, for instance, if you have a rule like the following:

rule "Every person named Mario is a male" when
    $person : Person( firstName == "Mario" )
then
    modify ( $person )  { setMale( true ) }
end

you won't have to add the no-loop attribute to it in order to avoid an infinite recursion because the engine recognizes that the pattern matching is done on the 'firstName' property while the RHS of the rule modifies the 'male' one. Note that this feature does not work for update(), and this is one of the reasons why we promote modify() since it encapsulates the field changes within the statement. Moreover, on Java classes, you can also annotate any method to say that its invocation actually modifies other properties. For instance in the former Person class you could have a method like:

@Modifies( { "firstName", "lastName" } )
public void setName(String name) {
    String[] names = name.split("\\s");
    this.firstName = names[0];
    this.lastName = names[1];
}

That means that if a rule has a RHS like the following:

modify($person) { setName("Mario Fusco") }

it will correctly recognize that the values of both properties 'firstName' and 'lastName' could have potentially been modified and act accordingly, not missing of reevaluating the patterns constrained on them. At the moment the usage of @Modifies is not allowed on fields but only on methods. This is coherent with the most common scenario where the @Modifies will be used for methods that are not related with a class field as in the Person.setName() in the former example. Also note that @Modifies is not transitive, meaning that if another method internally invokes the Person.setName() one it won't be enough to annotate it with @Modifies( { "name" } ), but it is necessary to use @Modifies( { "firstName", "lastName" } ) even on it. Very likely @Modifies transitivity will be implemented in the next release.

For what regards nested accessors, the engine will be notified only for top level fields. In other words a pattern matching like:

Person ( address.city.name == "London ) 

will be revaluated only for modification of the 'address' property of a Person object. In the same way the constraints analysis is currently strictly limited to what there is inside a pattern. Another example could help to clarify this. An LHS like the following:

$p : Person( )
Car( owner = $p.name )

will not listen on modifications of the person's name, while this one will do:

Person( $name : name )
Car( owner = $name )

To overcome this problem it is possible to annotate a pattern with @watch as it follows:

$p : Person( ) @watch ( name )
Car( owner = $p.name )

Indeed, annotating a pattern with @watch allows you to modify the inferred set of properties for which that pattern will react. Note that the properties named in the @watch annotation are actually added to the ones automatically inferred, but it is also possible to explicitly exclude one or more of them prepending their name with a ! and to make the pattern to listen for all or none of the properties of the type used in the pattern respectively with the wildcrds * and !*. So, for example, you can annotate a pattern in the LHS of a rule like:

// listens for changes on both firstName (inferred) and lastName
Person( firstName == $expectedFirstName ) @watch( lastName )

// listens for all the properties of the Person bean
Person( firstName == $expectedFirstName ) @watch( * )

// listens for changes on lastName and explicitly exclude firstName
Person( firstName == $expectedFirstName ) @watch( lastName, !firstName )

// listens for changes on all the properties except the age one
Person( firstName == $expectedFirstName ) @watch( *, !age )

Since doesn't make sense to use this annotation on a pattern using a type not annotated with @PropertyReactive the rule compiler will raise a compilation error if you try to do so. Also the duplicated usage of the same property in @watch (for example like in: @watch( firstName, ! firstName ) ) will end up in a compilation error. In a next release we will make the automatic detection of the properties to be listened smarter by doing analysis even outside of the pattern.

It also possible to enable this feature by default on all the types of your model or to completely disallow it by using on option of the KnowledgeBuilderConfiguration. In particular this new PropertySpecificOption can have one of the following 3 values:

- DISABLED => the feature is turned off and all the other related annotations are just ignored
- ALLOWED => this is the default behavior: types are not property reactive unless they are not annotated with @PropertySpecific
- ALWAYS => all types are property reactive by default

So, for example, to have a KnowledgeBuilder generating property reactive types by default you could do:

KnowledgeBuilderConfiguration config = KnowledgeBuilderFactory.newKnowledgeBuilderConfiguration();
config.setOption(PropertySpecificOption.ALWAYS);
KnowledgeBuilder kbuilder = KnowledgeBuilderFactory.newKnowledgeBuilder(config);

In this last case it will be possible to disable the property reactivity feature on a specific type by annotating it with @ClassReactive.

Using the new fluent simulation testing, you can test your rules in unit tests more easily:

    @Test

    public void rejectMinors() {
        SimulationFluent simulationFluent = new DefaultSimulationFluent();
        Driver john = new Driver("John", "Smith", new LocalDate().minusYears(10));
        Car mini = new Car("MINI-01", CarType.SMALL, false, new BigDecimal("10000.00"));
        PolicyRequest johnMiniPolicyRequest = new PolicyRequest(john, mini);
        johnMiniPolicyRequest.addCoverageRequest(new CoverageRequest(CoverageType.COLLISION));
        johnMiniPolicyRequest.addCoverageRequest(new CoverageRequest(CoverageType.COMPREHENSIVE));
        simulationFluent
        .newKnowledgeBuilder()
            .add(ResourceFactory.newClassPathResource("org/drools/examples/carinsurance/rule/policyRequestApprovalRules.drl"),
                    ResourceType.DRL)
            .end()
        .newKnowledgeBase()
            .addKnowledgePackages()
            .end()
        .newStatefulKnowledgeSession()
            .insert(john).set("john")
            .insert(mini).set("mini")
            .insert(johnMiniPolicyRequest).set("johnMiniPolicyRequest")
            .fireAllRules()
            .test("johnMiniPolicyRequest.automaticallyRejected == true")
            .test("johnMiniPolicyRequest.rejectedMessageList.size() == 1")
            .end()
        .runSimulation();
    }

You can even test your CEP rules in unit tests without suffering from slow tests:

    @Test

    public void lyingAboutAge() {
        SimulationFluent simulationFluent = new DefaultSimulationFluent();
        Driver realJohn = new Driver("John", "Smith", new LocalDate().minusYears(10));
        Car realMini = new Car("MINI-01", CarType.SMALL, false, new BigDecimal("10000.00"));
        PolicyRequest realJohnMiniPolicyRequest = new PolicyRequest(realJohn, realMini);
        realJohnMiniPolicyRequest.addCoverageRequest(new CoverageRequest(CoverageType.COLLISION));
        realJohnMiniPolicyRequest.addCoverageRequest(new CoverageRequest(CoverageType.COMPREHENSIVE));
        realJohnMiniPolicyRequest.setAutomaticallyRejected(true);
        realJohnMiniPolicyRequest.addRejectedMessage("Too young.");
        Driver fakeJohn = new Driver("John", "Smith", new LocalDate().minusYears(30));
        Car fakeMini = new Car("MINI-01", CarType.SMALL, false, new BigDecimal("10000.00"));
        PolicyRequest fakeJohnMiniPolicyRequest = new PolicyRequest(fakeJohn, fakeMini);
        fakeJohnMiniPolicyRequest.addCoverageRequest(new CoverageRequest(CoverageType.COLLISION));
        fakeJohnMiniPolicyRequest.addCoverageRequest(new CoverageRequest(CoverageType.COMPREHENSIVE));
        fakeJohnMiniPolicyRequest.setAutomaticallyRejected(false);
        simulationFluent
        .newStep(0)
        .newKnowledgeBuilder()
            .add(ResourceFactory.newClassPathResource("org/drools/examples/carinsurance/cep/policyRequestFraudDetectionRules.drl"),
                    ResourceType.DRL)
            .end()
        .newKnowledgeBase()
            .addKnowledgePackages()
            .end(World.ROOT, KnowledgeBase.class.getName())
        .newStatefulKnowledgeSession()
            .end()
        .newStep(1000)
        .getStatefulKnowledgeSession()
            .insert(realJohn).set("realJohn")
            .insert(realMini).set("realMini")
            .insert(realJohnMiniPolicyRequest).set("realJohnMiniPolicyRequest")
            .fireAllRules()
            .test("realJohnMiniPolicyRequest.requiresManualApproval == false")
            .end()
        .newStep(5000)
        .getStatefulKnowledgeSession()
            .insert(fakeJohn).set("fakeJohn")
            .insert(fakeMini).set("fakeMini")
            .insert(fakeJohnMiniPolicyRequest).set("fakeJohnMiniPolicyRequest")
            .fireAllRules()
            .test("fakeJohnMiniPolicyRequest.requiresManualApproval == true")
            .end()
        .runSimulation();
    }

Untill now, implementing TSP or Vehicle Routing like problems in Planner was hard. The new chaining support makes it easy.

You simply declare that a planning variable (previousAppearance) of this planning entity (VrpCustomer) is chained and therefor possibly referencing another planning entity (VrpCustomer) itself, creating a chain with that entity.

public class VrpCustomer implements VrpAppearance {


    ...
    @PlanningVariable(chained = true)
    @ValueRanges({
            @ValueRange(type = ValueRangeType.FROM_SOLUTION_PROPERTY, solutionProperty = "vehicleList"),
            @ValueRange(type = ValueRangeType.FROM_SOLUTION_PROPERTY, solutionProperty = "customerList",
                    excludeUninitializedPlanningEntity = true)})
    public VrpAppearance getPreviousAppearance() {
        return previousAppearance;
    }
    ...
}

This triggers automatic chain correction:

Without any extra boilerplate code, this is compatible with:

For more information, read the Planner reference manual.

Currently, when you in a RHS you call modify() on a given fact it triggers a revaluation of all Patterns of the matching object type in the knowledeg base. As some have found this can be a problem, forcing you to split up your objects into smaller 1 to 1 objects, to avoid unwanted evaluation of objects like recursion or excessive evaluation issues. This new feature allows a pattern to react (and then be re-eavluated) only to modifications made on fields constrained or bound inside the pattern itself.

The implementation is bit mask based, so very efficient. When the engine executes a modify statement it uses a bit mask of fields being changed, the pattern will only respond if it has an overlapping bit mask. This does not work for update(), and is one of the reason why we promote modify() as it encapsulates the field changes within the statement.

feature is off by default in order to make the behavior of the rule engine backward compatible with the former releases. If you want to activate it on a specific bean you have to annotate it with @propertySpecific. This annotation works both on drl type declarations:

declare Person
            @propertySpecific
            firstName : String
            lastName : String
        end

and on Java classes:

@PropertySpecific
        public static class Person {
            private String firstName;
            private String lastName;
        }

Given this simple type declaration, the most evident advantage of this new Property Specific feature, will be, for instance, that the following rule:

rule "Modify last name" when
        Person( firstName == "Mario" )
        then
        modify($person) { setLastName("Fusco") }
        end

will no longer end up in an infinite loop.

Moreover on Java classes you can also annotate any method to say that its invocation actually modifies other properties. For instance in the former Person class you could have a method like:

@Modifies( { "firstName", "lastName" } )
        public void setName(String name) {
        String[] names = name.split("\\s");
        this.firstName = names[0];
        this.lastName = names[1];
        }

That means that if a rule has a RHS like the following:

modify($person) { setName("Mario Fusco") }

it will correctly recognize that both the firstName and lastName have been modified and act accordingly. Of course the @Modifies annotation on a method has no effect if the declaring class isn't annotated with @PropertySpecific.

The third newly introduced annotation is on patterns and allows you to modify the inferred set of properties "listened" by it. So, for example, you can annotate a pattern in the LHS of a rule like:

Person( firstName == $expectedFirstName ) @watch( lastName ) // --> listens for changes on both firstName (inferred) and lastName
        Person( firstName == $expectedFirstName ) @watch( * ) // --> listens for all the properties of the Person bean
        Person( firstName == $expectedFirstName ) @watch( lastName, !firstName ) // --> listens for changes on lastName and explicitly exclude firstName
        Person( firstName == $expectedFirstName ) @watch( *, !age ) // --> listens for changes on all the properties except the age one

The usage of @watch on a type not annotated as @PropertySpecific will raise a compilation error. The same will happen if you mention an unknown property name inside @watch or use the same name more than once like for example in: @watch( lastName, !lastName )

The simulation project that was first started in 2009, http://blog.athico.com/2009/07/drools-simulation-and-test-framework.html, has undergone an over haul and is now in a usable state. We have not yet promoted this to knowledge-api, so it's considered unstable and will change during the beta process. For now though, the adventurous people can take a look at the unit tests and start playing.

The Simulator runs the Simulation. The Simulation is your scenario definition. The Simulation consists of 1 to n Paths, you can think of a Path as a sort of Thread. The Path is a chronological line on which Steps are specified at given temporal distances from the start. You don't specify a time unit for the Step, say 12:00am, instead it is always a relative time distance from the start of the Simulation (note: in Beta2 this will be relative time distance from the last step in the same path). Each Step contains one or more Commands, i.e. create a StatefulKnowledgeSession or insert an object or start a process. These are the very same commands that you would use to script a knowledge session using the batch execution, so it's re-using existing concepts.

  • 1.1 Simulation

    • 1..n Paths

      • 1..n Steps

        • 1..n Commands

All the steps, from all paths, are added to a priority queue which is ordered by the temporal distance, and allows us to incrementally execute the engine using a time slicing approach. The simulator pops of the steps from the queue in turn. For each Step it increments the engine clock and then executes all the Step's Commands.

Here is an example Command (notice it uses the same Commands as used by the CommandExecutor):

new InsertObjectCommand( new Person( "darth", 97 ) )

Commands can be grouped together, especially Assertion commands, via test groups. The test groups are mapped to JUnit "test methods", so as they pass or fail using a specialised JUnit Runner the Eclipse GUI is updated - as illustrated in the above image, showing two passed test groups named "test1" and "test2".

Using the JUnit integration is trivial. Just annotate the class with @RunWith(JUnitSimulationRunner.class). Then any method that is annotated with @Test and returns a Simulation instance will be invoked executing the returned Simulation instance in the Simulator. As test groups are executed the JUnit GUI is updated.

When executing any commands on a KnowledgeBuilder, KnowledgeBase or StatefulKnowledgeSession the system assumes a "register" approach. To get a feel for this look at the org.drools.simulation.impl.SimulationTest at github (path may change over time).

cmds.add( new NewKnowledgeBuilderCommand( null ) );
cmds.add( new SetVariableCommandFromLastReturn( "path1",
                                                KnowledgeBuilder.class.getName() ) );

cmds.add( new KnowledgeBuilderAddCommand( ResourceFactory.newByteArrayResource( str.getBytes() ),
                                          ResourceType.DRL, null ) );

Notice the set command. "path1" is the context, each path has it's own variable context. All paths inherit from a "root" context. "KnowledgeBuilder.class.getName() " is the name that we are setting the return value of the last command. As mentioned before we consider the class names of those classes as registers, any further commands that attempt to operate on a knowledge builder will use what ever is assigned to that, as in the case of KnowledgeBuilderAddCommand. This allows multiple kbuilders, kbases and ksessions to exist in one context under different variable names, but only the one assigned to the register name is the one that is currently executed on.

The code below show the rough outline used in SimulationTest:

Simulation simulation = new SimulationImpl();
PathImpl path = new PathImpl( simulation,
                              "path1" );                              
simulation.getPaths().put( "path1",
                           path );                                      
List<Step> steps = new ArrayList<Step>();
path.setSteps( steps );
List<Command> cmds = new ArrayList<Command>();
.... add commands to step here ....
// create a step at temporal distance of 2000ms from start
steps.add( new StepImpl( path,
                         cmds,
                         2000 ) ); 

We know the above looks quite verbose. SimulationTest just shows our low level canonical model, the idea is that high level representations are built ontop of this. As this is a builder API we are currently focusing on two sets of fluents, compact and standard. We will also work on a spreadsheet UI for building these, and eventually a dedicated textual dsl.

The compact fluent is designed to provide the absolute minimum necessary to run against a single ksession. A good place to start is org.drools.simulation.impl.CompactFluentTest, a snippet of which is shown below. Notice we set "yoda" to "y" and can then assert on that. Currently inside of the test string it executes using mvel. The eventual goal is to build out a set of hamcrest matchers that will allow assertions against the state of the engine, such as what rules have fired and optionally with with data.

FluentCompactSimulation f = new FluentCompactSimulationImpl();
f.newStatefulKnowledgeSession()
    .getKnowledgeBase()
    .addKnowledgePackages( ResourceFactory.newByteArrayResource( str.getBytes() ),
                           ResourceType.DRL )
    .end()
    .newStep( 100 ) // increases the time 100ms
    .insert( new Person( "yoda",
                         150 ) ).set( "y" )
    .fireAllRules()
    // show testing inside of ksession execution
    .test( "y.name == 'yoda'" )
    .test( "y.age == 160" );

Note that the test is not executing at build time, it's building a script to be executed later. The script underneath matches what you saw in SimulationTest. Currently the way to run a simulation manually is shown below. Atlhough you already saw in SimulationTest that JUnit will execute these automatically. We'll improve this over itme.

SimulationImpl sim = (SimulationImpl) ((FluentCompactSimulationImpl) f).getSimulation();
Simulator simulator = new Simulator( sim,
                                     new Date().getTime() );
simulator.run();

The standard fluent is almost a 1 to 1 mapping to the cannonical path, step and command structure in SimulationTest- just more compact. Start by looking in org.drools.simulation.impl.StandardFluentTest. This fluent allows you to run any number of paths and steps, along with a lot more control over multiple kbuilders, kbases and ksessions.

FluentStandardSimulation f = new FluentStandardSimulationImpl();      
f.newPath("init")
     .newStep( 0 )
          // set to ROOT, as I want paths to share this
         .newKnowledgeBuilder()
             .add( ResourceFactory.newByteArrayResource( str.getBytes() ),
                   ResourceType.DRL )
         .end(ContextManager.ROOT, KnowledgeBuilder.class.getName() )
         .newKnowledgeBase()
             .addKnowledgePackages()
         .end(ContextManager.ROOT, KnowledgeBase.class.getName() )
    .end()
 .newPath( "path1" )
    .newStep( 1000 )
        .newStatefulKnowledgeSession()
            .insert( new Person( "yoda", 150 ) ).set( "y" )
            .fireAllRules()
            .test( "y.name == 'yoda'" )
            .test( "y.age == 160" )
        .end()
    .end()
 .newPath( "path2" )
    .newStep( 800 )
        .newStatefulKnowledgeSession()
            .insert( new Person( "darth", 70 ) ).set( "d" )
            .fireAllRules()
            .test( "d.name == 'darth'" )
            .test( "d.age == 80" )
        .end()
    .end()
 .end

There is still an aweful lot to do, this is designed to eventually provide a unified simulation and testing environment for rules, workflow and event processing over time, and eventually also over distributed architectures.

  • Flesh out the api to support more commands, and also to encompass jBPM commands

  • Improve out of the box usability, including moving interfaces to knowlege-api and hiding "new" constructors with factory methods

  • Commands are already marshallable to json and xml. They should be updated to allow full round tripping from java api commands and json/xml documents.

  • Develop hamcrest matchers for testing state

    • What rule(s) fired, including optionally what data was used with the executing rule (Drools)

    • What rules are active for a given fact

    • What rules activated and de-activated for a given fact change

    • Process variable state (jBPM)

    • Wait node states (jBPM)

  • Design and build tabular authoring tools via spreadsheet, targetting the web with round tripping to excel.

  • Design and develop textual DSL for authoing - maybe part of DRL (long term task).

5.3 Introduces the declarative agenda, where rules can be used to control which rules can fire and when. While this will add a lot more overhead than the simple use of salience, the advantage is it is declarative and thus more readable and maintainable and should allow more use cases to be achieved in a simpler fashion.

This feature is off by default and must be explicitly enabled, that is because it is considered highly experimental for the moment and will be subject to change.


The basic idea is:

  • All matched rule's Activations are inserted into WorkingMemory as facts. So you can now match against an Activation. The rule's metadata and declarations are available as fields on the Activation object.

  • You can use the kcontext.blockActivation( Activation match ) for the current rule to block the selected activation. Only when that rule becomes false will the activation be eligible for firing. If it is already eligible for firing and is later blocked, it will be removed from the agenda until it is unblocked.

  • An activation may have multiple blockers and a count is kept. All blockers must became false for the counter to reach zero to enable the Activation to be eligible for firing.

  • kcontext.unblockAllActivations( $a ) is an over-ride rule that will remove all blockers regardless

  • An activation may also be cancelled, so it never fires with cancelActivation

  • An unblocked Activation is added to the Agenda and obeys normal salience, agenda groups, ruleflow groups etc.

  • @activationListener('direct') allows a rule to fire as soon as it's matched, this is to be used for rules that block/unblock activations, it is not desirable for these rules to have side effects that impact else where. The name may change later, this is actually part of the pluggable terminal node handlers I made, which is an "internal" feature for the moment.


Here is a basic example that will block all activations from rules that have metadata @department('sales'). They will stay blocked until the blockerAllSalesRules rule becomes false, i.e. "go2" is retracted.


This example shows how you can use active property to count the number of active or inactive (already fired) activations.


Guvnor continues to grow and improve with every passing release. One of the requirements we have is to move Guvnor away from a pure "Rules" or "Knowledge" repository management and authoring environment to a more generic one, in which consumers of different technologies can tailor their experience to their domain's requirements.

One of the more noticeable changes we are making to support this has been to de-couple asset types and groups from the code. What was once previously static code is now defined by compile-time configuration. Due to limitations in GWT (no client-side reflection), the technology with which Guvnor is implemented, we are unfortunately not able to offer runtime configuration at this moment.

The asset groups are configurable within source file; src/main/resources/drools-asseteditors.xml. This file is used at GWT compile time to wire-up asset types with their respective editor, group and group icon. An example extract from the foregoing file looks like this:-

<asseteditor>
    <class>org.drools.guvnor.client.modeldriven.ui.RuleModeller</class>
    <format>brl</format>
    <icon>images.ruleAsset()</icon> <title>constants.BusinessRuleAssets()</title>
</asseteditor>

To emphasis the separation, asset groups have become their own "editor" appearing as a tab in Guvnor's main, central panel.


The format of the new screen is being tried for 5.3.0.Beta1. There has been some discussion whether a single table containing all assets would be better - with collapsible rows to group different types of asset. The immediate problem with this approach is however that finding different asset types on a "paged table" becomes more cumbersome for the user; as they'd have to sort by type and page through.

We therefore ask for community feedback.

Patterns now support positional arguments on type declarations.

Positional arguments are ones where you don't need to specify the field name, as the position maps to a known named field. i.e. Person( name == "mark" ) can be rewritten as Person( "mark"; ). The semicolon ';' is important so that the engine knows that everything before it is a positional argument. Otherwise we might assume it was a boolean expression, which is how it could be interpretted after the semicolon. You can mix positional and named arguments on a pattern by using the semicolon ';' to separate them. Any variables used in a positional that have not yet been bound will be bound to the field that maps to that position.

declare Cheese
    name : String
    shop : String
    price : int
end

The default order is the declared order, but this can be overiden using @Position

declare Cheese
    name : String @position(1)
    shop : String @position(2)
    price : int @position(0)
end

The @Position annotation, in the org.drools.definition.type package, can be used to annotate original pojos on the classpath. Currently only fields on classes can be annotated. Inheritence of classes is supported, but not interfaces of methods yet.

Example patterns, with two constraints and a binding. Remember semicolon ';' is used to differentiate the positional section from the named argument section. Variables and literals and expressions using just literals are supported in posional arguments, but not variables.

Cheese( "stilton", "Cheese Shop", p; )
Cheese( "stilton", "Cheese Shop"; p : price )
Cheese( "stilton"; shop == "Cheese Shop", p : price )
Cheese( name == "stilton"; shop == "Cheese Shop", p : price )

Drools now provides Prolog style derivation queries, as an experimental feature. What this means is that a query or the 'when' part of a rule may call a query, via a query element. This is also recursive so that a query may call itself. A query element may be prefixed with a question mark '?' which indicuates that we have a pattern construct that will pull data, rather than the normal reactive push nature of patterns. If the ? is ommitted the query will be executed as a live "open query" with reactiveness, similar to how normal patterns work.

A key aspect of BC is unification. This is where a query parameter may be bound or unbound, when unbound it is considered an output variable and will bind to each found value.

In the example below x and y are parameters. Unification is done by subsequent bindings inside of patterns. If a value for x is passed in, it's as though the pattern says "thing == x". If a value for x is not passed in it's as though "x: thing" and x will be bound to each found thing.

Because Drools does not allow multiple bindings on the same variable we introduce ':=' unification symbol to support this.

declare Location
    thing : String 
    location : String 
end

query isContainedIn( String x, String y ) 
    Location( x := thing, y := location)
    or 
    ( Location(z := thing, y := location) and ?isContainedIn( x := x, z := y ) )
end

Positional and mixed positional/named are supported.

declare Location
    thing : String 
    location : String 
end

query isContainedIn( String x, String y ) 
    Location(x, y;)
    or 
    ( Location(z, y;) and ?isContainedIn(x, z;) )
end

Here is an example of query element inside of a rule using mixed positional/named arguments.

package org.drools.test

import java.util.List
import java.util.ArrayList

dialect "mvel"

declare Here
   place : String
end

declare Door
   fromLocation : String
   toLocation : String
end

declare Location
   thing : String
   location : String
end

declare Edible
   thing : String
end

query connect( String x, String y )
   Door(x, y;)
   or 
   Door(y, x;)
end

query whereFood( String x, String y )
   ( Location(x, y;) and
      Edible(x;) )
   or 
   ( Location(z, y;) and
      whereFood(x, z;) )
end

query look(String place, List things, List food, List exits)
   Here(place;)
   things := List() from accumulate( Location(thing, place;), collectList( thing ) )
   food := List() from accumulate( ?whereFood(thing, place;), collectList( thing ) )
   exits := List() from accumulate( ?connect(place, exit;), collectList( exit ) )
end

rule reactiveLook 
   when
      Here( $place : place)
      ?look($place, $things; $food := food, $exits := exits)
   then
      System.out.println( \"You are in the \" + $place);
      System.out.println( \"  You can see \" + $things );
      System.out.println( \"  You can eat \" + $food );
      System.out.println( \"  You can go to \" + $exits );
end

As previously mentioned you can use live "open" queries to reactively receive changes over time from the query results, as the underlying data it queries against changes. Notice the "look" rule calls the query without using '?'.

query isContainedIn( String x, String y ) 
    Location(x, y;)
    or 
    ( Location(z, y;) and isContainedIn(x, z;) )
end

rule look when 
    Person( $l : likes ) 
    isContainedIn( $l, 'office'; )
then
   insertLogical( $l + 'is in the office' );
end 

Literal expressions can passed as query arguments, but at this stage you cannot mix expressions with variables.

It is possible to call queries from java leaving arguments unspecified using the static field org.drools.runtime.rule.Variable.v - note you must use 'v' and not an alternative instanceof Variable. The following example will return all objects contained in the office.

results = ksession.getQueryResults( "isContainedIn", new Object[] {  Variable.v, "office" } );
l = new ArrayList<List<String>>();
for ( QueryResultsRow r : results ) {
    l.add( Arrays.asList( new String[] { (String) r.get( "x" ), (String) r.get( "y" ) } ) );
}  

The algorithm uses stacks to handle recursion, so the method stack will not blow up.

The existing Guided Decision Table has been replaced to provide a foundation on which to build our future guided Decision Table toolset. The initial release largely provides an equivalent feature-set to the obsolete Guided Decision Table with a few improvements, as explained in more detail below. A change from the legacy table was essential for us to begin to realise our desire to provide the number one web-based decision table available. With this foundation we will be able to expand the capabilities of our Guided Decision Table toolset to provide a feature rich, user-friendly environment.


A couple of maven artifacts (jars, wars, ...) have been renamed so it is more clear what they do. Below is the list of the GAV changes, adjust your pom.xml files accordingly when upgrading to the new version.

Table 4.2. Maven GAV changes overview

Old groupIdOld artifactIdNew groupIdNew artifactId
org.droolsdrools (the parent pom)org.droolsdroolsjbpm-parent
org.droolsdrools-apiorg.droolsknowledge-api
org.droolsdrools-docs-introductionorg.droolsdroolsjbpm-introduction-docs
org.droolsdrools-examples-drlorg.droolsdrools-examples
org.droolsdrools-examples-fusion / drools-examples-drl (jBPM using parts)org.droolsdroolsjbpm-integration-examples
org.droolsdrools-docs-expertorg.droolsdroolsjbpm-expert-docs
org.droolsdrools-docs-fusionorg.droolsdroolsjbpm-fusion-docs
org.droolsdrools-repositoryorg.droolsguvnor-repository
org.droolsdrools-ide-commonorg.droolsdroolsjbpm-ide-common
org.droolsdrools-guvnororg.droolsguvnor-webapp
org.jboss.drools.guvnor.toolsguvnor-importerorg.droolsguvnor-bulk-importer
org.droolsdrools-docs-guvnororg.droolsguvnor-docs
org.droolsdrools-serverorg.droolsdrools-camel-server
org.droolsdrools-docs-integrationorg.droolsdroolsjbpm-integration-docs
org.droolsdrools-flow-coreorg.jbpmjbpm-flow
org.droolsdrools-flow-compilerorg.jbpmjbpm-flow-builder
org.droolsdrools-bpmn2org.jbpmjbpm-bpmn2
org.droolsdrools-flow-persistence-jpaorg.jbpmjbpm-persistence-jpa
org.droolsdrools-bamorg.jbpmjbpm-bam
org.droolsdrools-process-taskorg.jbpmjbpm-human-task
org.droolsdrools-gwt-consoleorg.jbpmjbpm-gwt-console
org.droolsdrools-gwt-formorg.jbpmjbpm-gwt-form
org.droolsdrools-gwt-graphorg.jbpmjbpm-gwt-graph
org.droolsdrools-gwt-warorg.jbpmjbpm-gwt-war
org.droolsdrools-gwt-server-warorg.jbpmjbpm-gwt-server-war
org.droolsdrools-workitemsorg.jbpmjbpm-workitems
org.droolsdrools-docs-floworg.jbpmjbpm-docs

For example: before, in your pom.xml files, you declared drools-api like this:

  <dependency>
    <groupId>org.drools</groupId>
    <artifactId>drools-api</artifactId>
    ...
  </dependency>

And now, afterwards, in your pom.xml files, you declare knowledge-api like this instead:

  <dependency>
    <groupId>org.drools</groupId>
    <artifactId>knowledge-api</artifactId>
    ...
  </dependency>

Drools now has extensive Spring support, the XSD can be found in the the drools-spring jar. The namespace is "http://drools.org/schema/drools-spring"

Example 4.7. KnowledgeBuilder example


<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:drools="http://drools.org/schema/drools-spring"       
       xmlns:camel="http://camel.apache.org/schema/spring"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
                           http://drools.org/schema/drools-spring http://anonsvn.jboss.org/repos/labs/labs/jbossrules/trunk/drools-container/drools-spring/src/main/resources/org/drools/container/spring/drools-spring-1.0.0.xsd
                           http://camel.apache.org/schema/spring http://camel.apache.org/schema/spring/camel-spring.xsd">
  <drools:resource id="resource1" type="DRL" source="classpath:org/drools/container/spring/testSpring.drl"/>

  <drools:kbase id="kbase1">
    <drools:resources>
      <drools:resource type="DRL" source="classpath:org/drools/container/spring/testSpring.drl"/>
      <drools:resource ref="resource1"/>
      <drools:resource source="classpath:org/drools/container/spring/IntegrationExampleTest.xls" type="DTABLE">
        <drools:decisiontable-conf input-type="XLS" worksheet-name="Tables_2" />
      </drools:resource>
    </drools:resources>

    <drools:configuration>
      <drools:mbeans enabled="true" />
      <drools:event-processing-mode mode="STREAM" />
    </drools:configuration>
  </drools:kbase>
</beans>

KnowledgeBase takes the following configurations: "advanced-process-rule-integration, multithread, mbeans, event-processing-mode, accumulate-functions, evaluators and assert-behavior".

From the the kbase reference ksessions can be created


Like KnowledgeBases Knowlege sessions can take a number of configurations, including "work-item-handlers, "keep-references", "clock-type", "jpa-persistence".


StatefulKnowledgeSessions can be configured for JPA persistence

Example 4.10. JPA configuration for StatefulKnowledgeSessions


<bean id="ds" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
  <property name="driverClassName" value="org.h2.Driver" />
  <property name="url" value="jdbc:h2:tcp://localhost/DroolsFlow" />
  <property name="username" value="sa" />
  <property name="password" value="" />
</bean>

<bean id="myEmf" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
  <property name="dataSource" ref="ds" />
  <property name="persistenceUnitName" value="org.drools.persistence.jpa.local" />
</bean>

<bean id="txManager" class="org.springframework.orm.jpa.JpaTransactionManager">
  <property name="entityManagerFactory" ref="myEmf" />
</bean>

<drools:ksession id="jpaSingleSessionCommandService" type="stateful" kbase="kbase1">
  <drools:configuration>
    <drools:jpa-persistence>
      <drools:transaction-manager ref="txManager" />
      <drools:entity-manager-factory ref="myEmf" />
      <drools:variable-persisters>
         <drools:persister for-class="javax.persistence.Entity" implementation="org.drools.persistence.processinstance.persisters.JPAVariablePersister"/>
         <drools:persister for-class="java.lang.String" implementation="org.drools.container.spring.beans.persistence.StringVariablePersister"/>
         <drools:persister for-class="java.io.Serializable" implementation="org.drools.persistence.processinstance.persisters.SerializableVariablePersister"/>
      </drools:variable-persisters>  
    </drools:jpa-persistence>
  </drools:configuration>    
</drools:ksession>

Knowledge Sessions can support startup batch scripts, previous versions used the "script" element name, this will be updated to "batch". The following commands are supported: "insert-object", "set-global", "fire-all-rules", "fire-until-halt", "start-process", "signal-event". Anonymous beans or named "ref" attributes may be used.


ExecutionNodes are supported in Spring , these provide a Context of registered ksessions; this can be used with Camel to provide ksession routing.


Spring can be combined with Camel to provide declarative rule services. a Camel Policy is added from Drools which provides magic for injecting the ClassLoader used by the ksession for any data formatters, it also augments the Jaxb and XStream data formatters. In the case lf Jaxb it adds additional Drools related path info and with XStream it registers Drools related converters and aliases.

You can create as many endpoints as you require, using different addresses. The CommandMessagBodyReader is needed to allow the payload to be handled by Camel.


Camel routes can then be attached to CXF endpoints, allowing you control over the payload for things like data formatting and executing against Drools ksessions. The DroolsPolicy adds some smarts to the route. If JAXB or XStream are used, it would inject custom paths and converters, it can also set the classloader too on the server side, based on the target ksession. On the client side it automatically unwrapes the Response object.

This example unmarshalls the payload using an augmented XStream DataFormat and executes it against the ksession1 instance. The "node" there refers to the ExecutionContext, which is a context of registered ksessions.


The Drools endpoint "drools:node/ksession1" consists of the execution node name followed by a separator and optional knowledge session name. If the knowledge session is not specified the route will look at the "lookup" attribute on the incoming payload instace or in the head attribute "DroolsLookup" to find it.

Drools has always had query support, but the result was returned as an iterable set; this makes it hard to monitor changes over time.

We have now complimented this with Live Querries, which has a listener attached instead of returning an iterable result set. These live querries stay open creating a view and publish change events for the contents of this view. So now you can execute your query, with parameters and listen to changes in the resulting view.


A Drools blog article contains an example of Glazed Lists integration for live queries,

http://blog.athico.com/2010/07/glazed-lists-examples-for-drools-live.html

Rule's now suport both interval and cron based timers, which replace the now deprecated duration attribute.


Interval "int:" timers follow the JDK semantics for initial delay optionally followed by a repeat interval. Cron "cron:" timers follow standard cron expressions:


Calendars can now controll when rules can fire. The Calendar api is modelled on Quartz http://www.quartz-scheduler.org/ :


Calendars are registered with the StatefulKnowledgeSession:


They can be used in conjunction with normal rules and rules including timers. The rule calendar attribute can have one or more comma calendar names.


Appearance has been cleaned, for example less pop ups. Reminders for save after changes in assets and information about actions that were taken, also better error reporting if something goes wrong.

Guided Editor has basic support for From, Accumulate and Collect Patterns. You can add any of these structures as regular Patterns. New expression builder component was created to add support for nested method calls of a variable. Using the “plus” button at the top of rule’s WHEN section or using the new “Add after” button present in every Pattern will open the popup to add new conditional elements to your rule. In the list of possible elements you will find three new entries: ”From”, “From Accumulate” and “From Collect”.

When you add a new “From” element, you will see something like the image below in the guided editor. The left pattern of the “From” conditional element is a regular Pattern. You can add there any type of conditional element you want.The right section of the “From” pattern is an expression builder.


When using 'from collect' In the left pattern you can choose from “java.util.Collection”, “java.util.List” or “java.util.Set” Fact Types. This Fact Types will be automatically included in the package’s Fact Types list.

The right pattern of the collect conditional element could be one of this patterns:

  • Fact Type Pattern

  • Free Form Expression

  • From Pattern

  • From Collect Pattern

  • From Accumulate Pattern


When using 'from accumulate' The left pattern could be any Fact Type Pattern. The right section of this conditional element is splited in two:


The left pattern could be any Fact Type Pattern. The right section of this conditional element is splited in two:

  • Source Pattern: (Bed $n, in the screenshot) could be any Fact Type, From, Collect or Accumulate pattern.

  • Accumulate function: Here you will find a tabbed panel where you can enter an accumulate function (sum() in the screenshot) or you can create an online custom function using the “Custom Code” tab.

Drools now has complete api/implementation separation that is no longer rules oriented. This is an important strategy as we move to support other forms of logic, such as workflow and event processing. The main change is that we are now knowledge oriented, instead of rule oriented. The module drools-api provide the interfaces and factories and we have made pains to provide much better javadocs, with lots of code snippets, than we did before. Drools-api also helps clearly show what is intended as a user api and what is just an engine api, drools-core and drools-compiler did not make this clear enough. The most common interfaces you will use are:

Factory classes, with static methods, provide instances of the above interfaces. A pluggable provider approach is used to allow provider implementations to be wired up to the factories at runtime. The Factories you will most commonly used are:


A Typical example to load a process resource. Notice the ResourceType is changed, in accordance with the Resource type:


'kbuilder', 'kbase', 'ksession' are the variable identifiers often used, the k prefix is for 'knowledge'.


It is also possible to configure a KnowledgeBase using configuration, via a xml change set, instead of programmatically.



The other big change for the KnowledgeAgent, compared to the RuleAgent, is that polling scanner is now a service. further to this there is an abstraction between the agent notification and the resource monitoring, to allow other mechanisms to be used other than polling.


There are two new interfaces added, ResourceChangeNotifier and ResourceChangeMonitor. KnowlegeAgents subscribe for resource change notifications using the ResourceChangeNotifier implementation. The ResourceChangeNotifier is informed of resource changes by the added ResourceChangeMonitors. We currently only provide one out of the box monitor, ResourceChangeScannerService, which polls resources for changes. However the api is there for users to add their own monitors, and thus use a push based monitor such as JMS.

ResourceFactory.getResourceChangeNotifierService().addResourceChangeMonitor( myJmsMonitor);

In addition to the ability of configuring options in drools through configuration files, system properties and by setting properties through the API setProperty() method, Drools-API now supports type safe configuration. We didn't want to add specific methods for each possible configuration methods for two reasons: it polutes the API and every time a new option is added to Drools, the API would have to change. This way, we followed a modular, class based configuration, where a new Option class is added to the API for each possible configuration, keeping the API stable, but flexible at the same time. So, in order to set configuration options now, you just need to use the enumerations or factories provided for each option. For instance, if you want to configure the knowledge base for assert behavior "equality" and to automatically remove identities from pattern matchings, you would just use the enums:


For options that don't have a predefined constant or can assume multiple values, a factory method is provided. For instance, to configure the alpha threshold to 5, just use the "get" factory method:


As you can see, the same setOption() method is used for the different possible configurations, but they are still type safe.

Batch Executor allows for the scripting of a Knowledge session using Commands. Both the StatelessKnowledgeSession and StatefulKnowledgeSession implement this interface. Commands are created using the CommandFactory and executed using the "execute" method, such as the following insert Command:


Typically though you will want to execute a batch of commands, this can be achieved via the composite Command BatchExecution. BatchExecutionResults is now used to handle the results, some commands can specify "out" identifiers which it used to add the result to the BatchExecutionResult. Handily querries can now be executed and results added to the BatchExecutionResult. Further to this results are scoped to this execute call and return via the BatchExecutionResults:


The CommandFactory details the supported commands, all of which can marshalled using XStream and the BatchExecutionHelper. This can be combined with the pipeline to automate the scripting of a session.


Using the above for a rulset that updates the price of a Cheese fact, given the following xml to insert a Cheese instance using an out-identifier:


We then get the following BatchExecutionResults:


The MarshallerFactory is used to marshal and unmarshal StatefulKnowledgeSessions. At the simplest it can be used as follows:


However with marshalling you need more flexibility when dealing with referenced user data. To achieve this we have the ObjectMarshallingStrategy interface. Two implementations are provided, but the user can implement their own. The two supplied are IdentityMarshallingStrategy and SerializeMarshallingStrategy. SerializeMarshallingStrategy is the default, as used in the example above and it just calls the Serializable or Externalizable methods on a user instance. IdentityMarshallingStrategy instead creates an int id for each user object and stores them in a Map the id is written to the stream. When unmarshalling it simply looks to the IdentityMarshallingStrategy map to retrieve the instance. This means that if you use the IdentityMarshallingStrategy it's stateful for the life of the Marshaller instance and will create ids and keep references to all objects that it attempts to marshal.


For added flexability we can't assume that a single strategy is suitable for this we have added the ObjectMarshallingStrategyAcceptor interface that each ObjectMarshallingStrategy has. The Marshaller has a chain of strategies and when it attempts to read or write a user object it iterates the strategies asking if they accept responsability for marshalling the user object. One one implementation is provided the ClassFilterAcceptor. This allows strings and wild cards to be used to match class names. The default is "*.*", so in the above the IdentityMarshallingStrategy is used which has a default "*.*" acceptor. But lets say we want to serialise all classes except for one given package, where we will use identity lookup, we could do the following:


The KnowlegeAgent is created by the KnowlegeAgentFactory. The KnowlegeAgent provides automatic loading, caching and re-loading, of resources and is configured from a properties files. The KnowledgeAgent can update or rebuild this KnowlegeBase as the resources it uses are changed. The strategy for this is determined by the configuration given to the factory, but it is typically pull based using regular polling. We hope to add push based updates and rebuilds in future versions. The Following example constructs an agent that will build a new KnowledgeBase from the files specified in the path String. It will poll those files every 30 seconds to see if they are updated. If new files are found it will construct a new KnowledgeBase, instead of updating the existing one, due to the "newInstance" set to "true" (however currently only the value of "true" is supported and is hard coded into the engine):

Example 4.48. Constructing an agent



// Set the interval on the ResourceChangeScannerService if you are to use it and default of 60s is not desirable.
ResourceChangeScannerConfiguration sconf = ResourceFactory.getResourceChangeScannerService().newResourceChangeScannerConfiguration();
sconf.setProperty( "drools.resource.scanner.interval",
                  "30" ); // set the disk scanning interval to 30s, default is 60s
ResourceFactory.getResourceChangeScannerService().configure( sconf );
KnowledgeBase kbase = KnowledgeBaseFactory.newKnowledgeBase();
KnowledgeAgentConfiguration aconf = KnowledgeAgentFactory.newKnowledgeAgentConfiguration();
aconf.setProperty( "drools.agent.scanDirectories",
                  "true" ); // we want to scan directories, not just files, turning this on turns on file scanning
aconf.setProperty( "drools.agent.newInstance",
                  "true" ); // resource changes results in a new instance of the KnowledgeBase being built,
                            // this cannot currently be set to false for incremental building
    
KnowledgeAgent kagent = KnowledgeAgentFactory.newKnowledgeAgent( "test agent", // the name of the agent
                                                                kbase, // the KnowledgeBase to use, the Agent will also monitor any exist knowledge definitions
                                                                aconf );
kagent.applyChangeSet( ResourceFactory.newUrlResource( url ) ); // resource to the change-set xml for the resources to add

KnowledgeAgents can take a empty KnowledgeBase or a populated one. If a populated KnowledgeBase is provided, the KnowledgeAgent will iterate KnowledgeBase and subscribe to the Resource that it finds. While it is possible for the KnowledgeBuilder to build all resources found in a directory, that information is lost by the KnowledgeBuilder so those directories will not be continuously scanned. Only directories specified as part of the applyChangeSet(Resource) method are monitored.

Drools 4.0 had simple "RuleFlow" which was for orchestrating rules. Drools 5.0 introduces a powerful (extensible) workflow engine. It allows users to specify their business logic using both rules and processes (where powerful interaction between processes and rules is possible) and offers a unified enviroment.

Drools 5.0 brings to the rules world the full power of events processing by supporting a number of CEP features as well as supporting events as first class citizens in the rules engine.

Drools 4.0 is a major update over the previous Drools 3.0.x series. A whole new set of features were developed which special focus on language expressiveness, engine performance and tools availability. The following is a list of the most interesting changes.

As mentioned before Drools 4.0 is a major update over the previous Drools 3.0.x series. Unfortunately, in order to achieve the goals set for this release, some backward compatibility issues were introduced, as discussed in the mail list and blogs.

This section of the manual is a work in progress and will document a simple how-to on upgrading from Drools 3.0.x to Drools 4.0.x.