Traits were introduced in the 5.3 release, and details on them can be found in the N&N for there. This release adds an example so that people have something simple to run, to help them understand. In the drools-examples source project open the classes and drl for the namespace "/org/drools/examples/traits". There you will find an example around classifications of students and workers.
rule "Students and Workers" no-loop when $p : Person( $name : name, $age : age < 25, $weight : weight ) then IWorker w = don( $p, IWorker.class, true ); w.setWage( 1200 ); update( w ); IStudent s = don( $p, IStudent.class, true ); s.setSchool( "SomeSchool" ); update( s ); end rule "Working Students" salience -10 when $s : IStudent( $school : school, $name : name, this isA IWorker, $wage : fields[ "wage" ] ) then System.out.println( $name + " : I have " + $wage + " to pay the fees at " + $school ); end
A two part detailed article has been written up at a blog, which will later be improved and rolled into the main documentation. For now you can read them here.
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 -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. 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 userbility, 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).
The Guided Decision Table editor and wizard now support the creation of "Limited Entry" tables.
With the introduction of support for Limited Entry, the decision table format was improved to better differentiate between Condition and Action columns. Furthermore the table header was improved to show more information for Action columns.
The ability to rearrange whole patterns as well as individual conditions in the constraints section of the table has been added. This allows the table author to arrange constraints to maximise performance of the resulting rules, by placing generalised constraints before more specific. Action columns can also be re-arranged. Both patterns and columns are re-arranged by dragging and dropping.
This release brings the ability to define Action columns to retract Facts.
If you are authoring an Extended Entry decision table the column definition contains basic information and the fact being retracted is held in the table itself.
If however you are authoring a Limited Entry decision table the Fact being retracted is defined in the column definition.
You can now bind fields in Conditions to variables. These variables can then be used in Predicate or Forumla conditions, and Work Item actions.
jBPM Work Items can now be used as Actions; and the corresponding Work Item Handler invoked at runtime. Work Item Handlers should be added to the runtime session as normal.
Work Item input parameters can either be defined as a literal value in the column definition or as a Fact or Fact Field binding.
New Actions have been created to perform the following functions:-
Execute a Work Item
Set the value of a field on an existing Fact to the value of a Work Item output (result) parameter.
Set the value of a field on a new Fact to the value of a Work Item output (result) parameter.
On the bottom of Web decision tables, there's now a button Analyze... which will check if there issues in your decision table, such as impossible matches and conflicting matches.
An impossible match is a row in a decision table that can never match. For example:
In the decision table above, row 2 is an impossible match, because there is no Person with a minimum age of 21 and a maximum age of 20.
Added support to create (PUT) and delete (DELETE) a Category
with rest/categories/{categoryName}
.
Removed buggy category
property from
Package
: it was null
upon GET
and ignored upon PUT.
The REST resource to get the assets for a certain category has
been moved from rest/categories/{categoryName}
to
rest/categories/{categoryName}/assets
.
The REST resources now properly encode and decode the URL's.
Note that URL path encoding is different than URL query encoding:
the former does not accept +
as encoding for
space.
This does not mean that Guvnor can not be made to work in AS 6. We are just focusing on JBoss AS 7 and Tomcat.
When using Rule Templates you can now define interpolation variables in free-form DRL blocks. Template Keys are formatted as documented in Drools Expert User Guide; i.e. @{keyName}. All Template Keys in 'free-form' LHS or RHS elements are considered 'Text' (i.e. you'll get a TextBox in the Template data screen). Data-types should be correctly escaped in the 'free-form' entry. For example: System.out.println("@{key}");
Planner now comes with 2 build-in move factories:
GenericChangeMoveFactory
and
GenericSwapMoveFactory
. Here's an example that uses
both of them:
<localSearch> <selector> <selector> <moveFactoryClass>org.drools.planner.core.move.generic.GenericChangeMoveFactory</moveFactoryClass> </selector> <selector> <moveFactoryClass>org.drools.planner.core.move.generic.GenericSwapMoveFactory</moveFactoryClass> </selector> </selector> ... </localSearch>
It's no longer required to write your own Move and MoveFactory implementations, but you still can (and mix those in too).
Debugging with Java is not working. Debugging with MVEL is not affected. This will be fixed in the future releases before 5.4.0.Final