KieScanner

If there is a universal truth about applications, it is that business rules will change over time. The criteria used to apply a discount, or even the amount of the discount itself, we are using today will—without any doubt—have to be modified tomorrow. We, as the architects and developers of these applications, have to be prepared for this.

Drools provides us with a way to mitigate this issue already. The separation that Drools introduces between the applications and the business logic allows us to decouple the changes happening in the business logic—which are usually frequent—from the changes happening in the application's infrastructure or UI, which are not so frequent.

Regardless of all the advantages provided by this separation, there is still one major problem that remains: every time our business logic (rules) changes, any KieContainer that referenced it has to be notified.

We already saw how a KieContainer can be manually updated when a new version of a KieModule is deployed, one of the limitations of this approach is that we have to manually notify each of the applications that depend on the modified KieModule. What if there were a way to automatically let out the applications to be notified when a KieModule that they depend on gets updated? Fortunately for us, Drools provides this mechanism out of the box, its name is KieScanner.

Note

In order to use KieScanner in our application, the org.kie::kie-ci artifact must be added to the application's classpath.

The KieScanner component in Drools is nothing but a wrapper around a KieContainer that can be configured to automatically detect changes in the resources that the container depends on. There is a catch though, the resources referenced by the KieContainer being monitored must be KieJars residing in a Maven repository. By default, kie-ci will use Maven's settings.xml in the .m2 folder of the user's home. This behavior can be overridden using the -Dkie.maven.settings.custom system property, as follows:

KieServices ks = KieServices.Factory.get();
KieContainer kieContainer = ks.newKieContainer(ks.newReleaseId("group.test","artifact-test", "1.0"));
KieScanner scanner = ks.newKieScanner(kieContainer);

As we can see, a KieScanner can be instantiated—just like most of the Drools 6 components—from the KieServices class. After KieScanner is instantiated, we have two options, we could either configure it to poll for new versions of the underlying KieJars on every fixed amount of milliseconds or we can manually force it to check for new versions on demand, as follows:

//Manually run a check for new versions
scanner.scanNow();
//Configure the scanner to check for new versions every 10 seconds
scanner.start(10_000);

If the start()method was used to start the KieScanner, the antagonic stop() method can be used to stop the polling mechanism.

When a new version of a KieJar is found by KieScanner, an incremental build resources is triggered. From this moment, all the new KieBases and KieSessions created from the KieContainer being wrapped will use the new version of the resources. It is important to mention that any pre-existing KieBase and KieSession will also be updated to the latest version of the resources found by the KieScanner.

At first, it is easy to think that an incremental build of a KieContainer could be useful for adding, modifying, or removing rules from it; however, the truth is that a more complex modification of the resources such as new type of declarations, modification on existing type declarations, addition of global variables and/or functions, and pretty much everything you could think of are also allowed to happen when a KieContainer is rebuilt.

Artifacts version resolution

The way KieScanner checks for new versions of the KieJars being monitored is according to Maven's rules for artifact versions: the newest version is always the one with the greatest version number, unless the version is a SNAPSHOT. If two artifacts have the same GroupId, ArtifactId and Version, the timestamp of each artifact is compared in order to determine which one is the newest. KieScanner only checks for versions in the KieJar that are compatible with the one defined in the KieContainer being wrapped. In other words, if the KieContainer is using version 1.0 of a particular KieJar, the corresponding KieScanner will check for new versions of the underlying KieJar that are compatible with 1.0.

The following tables show what compatible version means in the context of different KieContainer versions and the versions of a newly deployed KieJar in Maven repository:

KieContainer version: 1.0

New KieJar version

KieScanner triggers?

Reason

0.9

No

0.9 is a different (lower) version than 1.0 (the version that the KieContainer is using).

1.0-SNAPSHOT

No

1.0-SNAPSHOT is considered a different version than 1.0 (the version that the KieContainer is using).

1.0

Yes

1.0 is the same version defined in KieContainer. The newly deployed version has a bigger timestamp than the one the KieContainer is using.

1.1

No

1.1 is a different (higher) version than 1.0 (the version that the KieContainer is using).

As we can see in the preceding table, KieScanner is only triggered when a new (according to its timestamp) version of the KieJar being monitored is detected in the Maven repository. As far as KieScanner concerns, SNAPSHOT versions are treated in the same way as the final ones. The following table shows the behavior of SNAPSHOT KieJar versions:

KieContainer version: 1.0-SNAPSHOT

New KieJar version

KieScanner triggers?

Reason

0.9

No

0.9 is a different (lower) version than 1.0-SNAPSHOT (the version that the KieContainer is using).

1.0-SNAPSHOT

Yes

1.0-SNAPSHOT is the same version defined in KieContainer. The newly deployed version has a bigger timestamp than the one the KieContainer is using.

1.0

No

1.0 is considered a different version than 1.0-SNAPSHOT (the version that the KieContainer is using).

1.1

No

Same as the previous case.

A KieScanner can be defined to use a fixed KieJar version (that is, 1.0 or 1.0-SNAPSHOT), but it could also be configured to use Maven's LATEST or RELEASE magic version name or even a range. These special version definitions are useful when we want to keep our KieContainers updated with the latest version—or a range of them—of a KieJar irrespective of its specific version number:

KieContainer version: LATEST/RELEASE (original KieJar version: 0.9)

New KieJar version

KieScanner triggers?

Reason

0.9

Yes

0.9 is the same version defined in KieContainer. The newly deployed version has a bigger timestamp than the one the KieContainer is using.

1.0-SNAPSHOT

No

KieScanner treats the LATEST version as RELEASE. Snapshot versions are not used when specifying these magic version names. Consider using ranges if you want SNAPSHOTS to be used.

1.0

Yes

1.0 is a different (higher) version than 0.9 (the version that the KieContainer is using).

1.1

Yes

Same as the previous case.

As noted in the preceding table, KieScanner differs from Maven's specification and treats LATEST magic version name in the same way that it treats the RELEASE: SNAPSHOT versions are being taken into account when using any of these magic version names.

Just like LATEST and RELEASE, magic version names are supported by KieScanner as well as the ranges (more about Maven versions and ranges can be found here: https://docs.oracle.com/middleware/1212/core/MAVEN/maven_version.htm#MAVEN8903). A version range allows us to specify a range of valid versions for the KieJar being used by the KieContainer being wrapped by a KieScanner. Ranges support both open and closed-ended ranges.

KieContainer version: (0.9), (original KieJar version: 0.9)

New KieJar version

KieScanner triggers?

Reason

0.9

Yes

0.9 is the same version defined in the KieContainer. The newly deployed version has a bigger timestamp than the one the KieContainer is using.

1.0-SNAPSHOT

Yes

1.0-SNAPSHOT is a different (higher) version than 0.9 (the version that the KieContainer is currently using).

1.0

Yes

1.0 is a different (higher) version than 1.0-SNAPSHOT (the version that the KieContainer is currently using).

1.1

Yes

1.1 is a different (higher) version than 1.0 (the version that the KieContainer is currently using).

In the preceding example, an open-ended version ([0.9,]) is specified in the KieContainer. The range is basically saying that the KieContainer allows any version higher than 0.9, including SNAPSHOT versions too.

Dealing with unexpected issues and errors

So far, we have covered how and when a KieContainer is updated when it is used in conjunction with a KieScanner; however, there is an important question that is still open: what happens if there is an error while upgrading a KieContainer to a newer version?

First thing we need to understand is the kind of errors could occur while upgrading the version of a KieJar. Generally speaking, we are talking about the compilation errors in the new version of the KieJar such as the following:

  • Syntax errors
  • Adding rules with duplicated names
  • Removing globals that are still being used, and so on.

KieScanner treats the whole process of a KieJar as an atomic action. When the upgrade of a KieContainer fails for any reason, the entire process is rolledback and the KieContainer remains in its original form. Any error generated during the upgrade process will be silently discarded.

Putting it all together

After the rather long introduction of KieScanner, it's time to put it all together in a unit test that we can play with.

In the chapter-03/chapter-03-tests project, there is a KieScannerTest.java test class. This class contains a kieContainerUpdateKieJarTest JUnit test. This test shows how a KieScanner can be used to update the KieJar that a KieContainer is using. The test also shows how, after the underlying KieContainer is updated, a previously created KieSession is automatically updated to the latest version of the KieJar being used by the KieContainer.

The test starts by creating two SILVER Customers (customerA and customerB) and an Order for each of them (orderA and orderB). The idea behind this unit test is to create a KieJar with a single rule that will apply a discount to Orders from SILVER Customers. The first version of the KieJar will give SILVER Customers a 10% discount on their Orders. The second version—the test will programmatically create and deploy a new version of the KieJar—will give SILVER Customers a discount of 25% instead:

ReleaseId releaseId = ks.newReleaseId(groupId, artifactId, version);
InternalKieModule originalKJar = createKieJar(ks, releaseId,createDiscountRuleForSilverCustomers(10.0));

The preceding lines (this is an extract of the code that you will find in the unit test) programmatically creates a KieJar with a single rule to apply 10% discount on SILVER Customers Orders:

repository.deployArtifact(releaseId, originalKJar, createKPom (fileManager, releaseId));
KieContainer kieContainer = ks.newKieContainer(releaseId);
KieScanner scanner = ks.newKieScanner(kieContainer);

Once the KieJar is created, it is deployed to a local Maven repository and a KieContainer is then created from it. The code excerpt also shows how the KieContainer is wrapped using a KieScanner. In real-life scenarios, we should start the KieScanner using its start method; however, given that we require our unit test to be deterministic, we don't want the KieScanner checking for updates in an asynchronous way. Instead of this, the test will use KieScanner's scanNow method when required, as follows:

KieSession ksession = kieContainer.newKieSession();
this.calculateAndAssertDiscount(customerA, orderA, ksession, 10.0);

From the generated KieContainer, we will create a new KieSession; the calculateAndAssertDiscount helper method is used to insert customerA and orderA in the KieSession and assert that the generated discount is 10.0, as shown in the following:

InternalKieModule newKJar = createKieJar(ks, releaseId, createDiscountRuleForSilverCustomers(25.0));
repository.deployArtifact(releaseId, newKJar, createKPom(fileManager, releaseId));

The next step is to create a different version of the same KieJar (same group ID, artifact ID, and version) this time, having a rule that will give 25% discount to SILVER Customers. Just like before, this KieJar is deployed in Maven's repository, overriding its previous version.

It's important to note that the KieContainer will not notice this change. The KieContainer was built using the previous version of the KieJar and will not update its content until explicitly told (either by the use of a KieScanner or by the KieContainer.updateToVersion method).

scanner.scanNow();
this.calculateAndAssertDiscount(customerB, orderB, ksession, 25.0);

In the preceding code, we can see how the test is explicitly telling the KieScanner to scan the monitored KieContainer and check for updates on the underlying KieJar. Note that, in contrast to KieScanner.start, KieScanner.scanNow is synchronous. After it returns, the KieContainer will be updated to the latest version of the KieJar detected by the scanner.

When the KieContainer gets updated to the latest version of the KieJar being used, any previously created KieBase—and thus, KieSession—will also be updated. The test demonstrates this behavior by inserting customerB and orderB in the same session that was previously used for customerA and orderA. The result, this time, will be different though. The KieSession will now contain the version of the rule that gives 25% discount instead of 10% and that is what the test is actually asserting.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.21.21.47