There are several ways of getting data out of your rules. They are:
- Invoke actions with side-effects.
- Set/modify global variables.
- Extract data from working memory using fact handles.
The first is the easiest and the one I would suggest doing. The second is how we used to do it back in the "old days" (10 or so years ago, with Drools 5.) The last is technically possibly but very complex and I've never done it.
I've covered all of these in the answer to this other question, which I believe to be a duplicate but can't vote for it as such since it has no upvoted answers.
Actions with side-effects
This is the recommended way of doing this. An action with a side effect is something that you call that can be seen outside of the rules. An extreme example could be saving to a database, but a simpler example might be adding an object to a list.
As an example, consider this toy situation. If a student has more than 3 unexcused absences, a note is generated and sent home with them for their parents. The following are two ways we might model this scenario. One rule adds the note to a variable in the student object itself. Another rule calls a utility function to send the note to the parents. The third adds the rule to a collections of notes.
rule "Unexcused absences: notify by adding note to the student record"
when
$student: Student( absences >= 3, $name: name )
then
$student.addNote( "Student '" + $name + "' has had too many unexcused absences");
end
rule "Unexcused absences: notify by email"
when
$student: Student( absences >= 3, $name: name )
$emailSystem: MailSystem() // Some utility class which can send emails
then
$emailSystem.sendNote(
$student,
"Student '" + $name + "' has had too many unexcused absences"
);
end
rule "Unexcused absences: append note to a list"
when
Student( absences >= 3, $name: name )
$notes: List()
then
$notes.add("Student '" + $name + "' has had too many unexcused absences.");
end
The way all of these rules work is that they change the data in either the external application (the mailing system) or the input objects (the student or the list of notes). After the rules finish firing, those side effects will be visible to the outside world.
Global variables
This is the old way of doing things, global variables. They're generally the same as static variables in Java, but you must not rely on them to track data between rule executions. (Just like with static variables and threads, what data is visible to the rules gets a little complicated.)
But if all we want to do is track rule output, this is a viable alternative.
First off, you set a global variable against the session before invoking the rules like this:
KieSession session = ruleBase.newStatefulSession();
session.insert(...); // insert data
session.setGlobal( "myGlobalFoo", value ); // sets the global; note the name must match the rule file!
session.fireAllRules();
Then in your rule file you'd declare your global at the top of your file, below your imports, using the global
keyword. The name of your global must exactly match what you pass in to the setGlobal
method.
Using the same example as before (student with 3+ absences getting a note sent home), we can use a global as follows. In this first example, we can use a List<string> notes
of notes sent home, and set that as a global:
global List notes;
rule "Student absences: add note to global List"
when
Student( absences >= 3, $name: name )
then
notes.add("Student '" + $name + "' has been absent too many times!");
end
Once all the rules are fired, the object that you passed into the global will have the values added to it. Note that it's important that you retain a reference to the object -- don't call something like session.setGlobal("notes", new ArrayList<>())
! You'll not have a reference to that notes list that way.
The KieSession javadoc talks a bit about globals, along with some cautions.
The third way is to extract the data using fact handles. This is not well documented and I've not done it personally, but it should be possible. Usually it is used to keep a "handle" on a piece of data being passed into working memory so you can fish it out for unit testing or whatnot. I think it may be possible to target and extract your newly instantiated data.
Personally I would not go this route since it's not a standard workflow and the documentation around these APIs is poor compared to the other parts of the Drools library (since this is primarily used internally and for testing.)