JBoss.orgCommunity Documentation

Chapter 4. Drools Release Notes

4.1. What is New and Noteworthy in Drools 6.0.0
4.1.1. Drools Expert
4.1.2. Guvnor
4.1.3. Drools Workbench
4.1.4. OptaPlanner (AKA Drools Planner)
4.1.5. Dashboard Builder
4.2. What is New and Noteworthy in Drools 5.5.0
4.2.1. Drools Expert
4.2.2. Guvnor
4.2.3. Planner
4.2.4. Drools Integration
4.3. What is New and Noteworthy in Drools 5.4.0
4.3.1. Drools Expert
4.3.2. Drools Fusion
4.3.3. Guvnor
4.3.4. Planner
4.3.5. 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

Many things are changing for Drools 6.0.

Along with the functional and feature changes we have restructured the Guvnor github repository to better reflect our new architecture. Guvnor has historically been the web application for Drools. It was a composition of editors specific to Drools, a back-end repository and a simplistic asset management system.

Things are now different.

For Drools 6.0 the web application has been extensively re-written to use UberFire that provides a generic Workbench environment, a Metadata Engine, Security Framework, a VFS API and clustering support.

Guvnor has become a generic asset management framework providing common services for generic projects and their dependencies. Drools use of both UberFire and Guvnor has born the Drools Workbench.

A picture always helps:


UberFire is the foundation of all components for both Drools and jBPM. Every editor and service leverages UberFire. Components can be mixed and matched into either a full featured application of used in isolation.

Guvnor adds project services and dependency management to the mix.

At present Guvnor consists of few parts; being principally a port of common project services that existed in the old Guvnor. As things settle down and the module matures pluggable workflow will be supported allowing for sensitive operations to be controlled by jBPM processes and rules. Work is already underway to include this for 6.0.

Both Drools and jBPM editors and services share the need for a common set of re-usable screens, services and widgets.

Rather than pollute Guvnor with screens and services needed only by Drools and jBPM this module contains such common dependencies.

It is possible to just re-use the UberFire and Guvnor stack to create your own project-based workbench type application and take advantage of the underlying services.

Drools Workbench is the end product for people looking for a web application that is composed of all Drools related editors, screens and services. It is equivalent to the old Guvnor.

Looking for the web application to accompany Drools Expert and Drools Fusion; an environment to author, test and deploy rules. This is what you're looking for.

KIE Drools Workbench (for want of a better name - it's amazing how difficult names can be) is an extension of Drools Workbench including jBPM Designer to support Rule Flow.

jBPM Designer, now being an UberFire compatible component, does not need to be deployed as a separate web application. We bundle it here, along with Drools as a convenience for people looking to author Rule Flows along side their rules.

This is the daddy of them all.

KIE Workbench is the composition of everything known to man; from both the Drools and jBPM worlds. It provides for authoring of projects, data models, guided rules, decision tables etc, test services, process authoring, a process run-time execution environment and human task interaction.

KIE Workbench is the old Guvnor, jBPM Designer and jBPM Console applications combined. On steroids.

UberFire VFS is a NIO.2 based API that provides a unified interface to access different file systems - by default it uses Git as its primary backend implematation. Resources (ie. files and directories) are represented, as proposed by NIO.2, as URIs. Here are some examples:

  • file:///path/to/some/file.txt

  • git://repository-name/path/to/file.txt

  • git://repository-name/path/to/dir/

  • git://my_branch@repository-name/path/to/file.txt

By using Git as its default implementation, UberFire VFS provides out-of-box a versioned system, which means that every information stored/deleted has its history preserved.

Important

Your system doesn't need to have GIT installed, UberFire uses JGIT internally, a 100% Java based Git implementation.

Drools Workbench is the corner stone of the new function and features for 6.0.

Many of the new concepts and functions are described in more detail in the following sections.

Project editor does what Package Editor did previously. It manages the the KIE projects. You can access the Project Editor from the menu bar Tools->Project Editor. The editor shows the configurations for the current active project and the content changes when you move around in your code repository.


Typically, a business process analyst or data analyst will capture the requirements for a process or appliaction and turn these into a formal set of data structures and their relationships. The new Data Modeller tool enables configuration of such data models (both logical and physical), without the need for explicit coding. Its main goals are to make data models into first class citizens in the process improvement cycle and allow for full process automation using data and forms, without the need for advanced development skills.

By default, whenever a new project is created, it automatically associates an empty data model to it. The currently active project's data model can be opened from the menu bar Tools-> Data Modeller:


The basic aspect of the Data Modeller is shown in the following screenshot:


The Data Modeller screen is divided into the following sections:

  • Model browser: the leftmost section, which allows creation of new data objects and where the existing ones are listed.

  • Object browser: the middle section, which displays a table with the fields of the data object that has been selected in the Model browser. This section also enables creating new attributes for the currently selected object.

  • Object / Attribute editor: the rightmost section. This is a tabbed editor where either the currently selected object's properties (as currently shown in the screenshot), or a previously selected object attribute's properties, can be modified.

Whenever a data model screen is activated, an additional entry will appear in the top menu, allowing creation of new data objects, as well as saving the model's modifications. Saving the model will also generate all its assets (pojo's), which will then become available to the rest of the tools.


Sometimes the constraint of having one single consequence for each rule can be somewhat limiting and leads to verbose and difficult to be maintained repetitions like in the following example:

rule "Give 10% discount to customers older than 60"
when
    $customer : Customer( age > 60 )
then
    modify($customer) { setDiscount( 0.1 ) };
end

rule "Give free parking to customers older than 60"
when
    $customer : Customer( age > 60 )
    $car : Car ( owner == $customer )
then
    modify($car) { setFreeParking( true ) };
end

It is already possible to partially overcome this problem by making the second rule extending the first one like in:

rule "Give 10% discount to customers older than 60"
when
    $customer : Customer( age > 60 )
then
    modify($customer) { setDiscount( 0.1 ) };
end

rule "Give free parking to customers older than 60"
    extends "Give 10% discount to customers older than 60"
when
    $car : Car ( owner == $customer )
then
    modify($car) { setFreeParking( true ) };
end

Anyway this feature makes it possible to define more labelled consequences other than the default one in a single rule, so, for example, the 2 former rules can be compacted in only one like it follows:

rule "Give 10% discount and free parking to customers older than 60"
when
    $customer : Customer( age > 60 )
    do[giveDiscount]
    $car : Car ( owner == $customer )
then
    modify($car) { setFreeParking( true ) };
then[giveDiscount]
    modify($customer) { setDiscount( 0.1 ) };
end

This last rule has 2 consequences, the usual default one, plus another one named "giveDiscount" that is activated, using the keyword do, as soon as a customer older than 60 is found in the knowledge base, regardless of the fact that he owns a car or not. The activation of a named consequence can be also guarded by an additional condition like in this further example:

rule "Give free parking to customers older than 60 and 10% discount to golden ones among them"
when
    $customer : Customer( age > 60 )
    if ( type == "Golden" ) do[giveDiscount]
    $car : Car ( owner == $customer )
then
    modify($car) { setFreeParking( true ) };
then[giveDiscount]
    modify($customer) { setDiscount( 0.1 ) };
end

The condition in the if statement is always evaluated on the pattern immediately preceding it. In the end this last, a bit more complicated, example shows how it is possible to switch over different conditions using a nested if/else statement:

rule "Give free parking and 10% discount to over 60 Golden customer and 5% to Silver ones"
when
    $customer : Customer( age > 60 )
    if ( type == "Golden" ) do[giveDiscount10]
    else if ( type == "Silver" ) break[giveDiscount5]
    $car : Car ( owner == $customer )
then
    modify($car) { setFreeParking( true ) };
then[giveDiscount10]
    modify($customer) { setDiscount( 0.1 ) };
then[giveDiscount5]
    modify($customer) { setDiscount( 0.05 ) };
end

Here the purpose is to give a 10% discount AND a free parking to Golden customers over 60, but only a 5% discount (without free parking) to the Silver ones. This result is achieved by activating the consequence named "giveDiscount5" using the keyword break instead of do. In fact do just schedules a consequence in the agenda, allowing the remaining part of the LHS to continue of being evaluated as per normal, while break also blocks any further pattern matching evaluation. Note, of course, that the activation of a named consequence not guarded by any condition with break doesn't make sense (and generates a compile time error) since otherwise the LHS part following it would be never reachable.

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. Although you already saw in SimulationTest that JUnit will execute these automatically. We'll improve this over time.

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 canonical 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 awful 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 knowledge-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, targeting the web with round tripping to excel.

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

Currently when in a RHS 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. 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.

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 reevaluated 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 wildcards * 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.

This API is experimental: future backwards incompatible changes are possible.

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();
    }

Until 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.

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.core.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.