It is a common requirement nowadays to access to our Rule Engine Instances as REST services. On this blog post I will show an extremely simple example on how you can expose your domain specific interfaces that you can use to encapsulate KieSessions. This post builds up on my previous three posts: first, second, third.

We will be using Wildfly Swarm to create a Fat-Jar that we will be able to run without the need of installing a full blown Java EE application Server. This approach is very good when you need full control about the REST interfaces that you want to expose to your client applications. For a more general use and for an already built in approach you can take a look a the new KIE Server, something that I will cover in future posts as well.

Introduction

One of the most interesting aspect of Drools is the fact that it can be embedded inside our services without too much overhead or complications. The fact that we can start as many instances as we want gives us the ultimate power to create and destroy KieSessions as we need.

The example that we are looking at here contains most of the pieces that you will find in real applications. And for that reason we have 3 different projects:

drools-user-model: this module contains our Business Entities, for this example just the User class.

drools-user-kjar: contains our rules. This package will be resolve by the KIE-CI module.

As you may notice the Service Layer (drools-jax-rs project) doesn’t need to know about the rules, because we are going to use KIE-CI to resolve them in the same way that we did that in my previous post. The Service Layer depends on the Model (drools-user-model) because the methods in the endpoint interface uses the User object as part of the contract of the service that is being provided. The KJAR, has an implicit dependency on the Model, just because the rules will need the Model classes to be compiled and executed, but there is no need to have a direct dependency at the project level.

The boundaries in the previous diagram is the Fat-Jar that we are going to generate with Wildfly Swarm Maven plugin, that will package us a Jar file that we can directly execute, without installing anything else in our environment besides Java.

Our (Micro)Service

The more I read about MicroServices the more I feel that Drools fits perfectly inside and around these services that we will be defining. There are several ways in which Drools can be used as part of our Services or to coordinate them based on business requirements. We will keep this example ultra simple, and for this reason, in this case, our service contract will look like this:

Our service will be in charge of categorise our Users based on their properties. From the clients point of view, it doesn’t really matter how this categorisation is done, as soon as the user is categorised correctly. Internally from our company, we know that these categories will change from time to time, so we have chosen to use Rules to do these categorisations. We will be able to update these rules as soon as the company decides to add, remove or even change the way that these categories are applied.

Internally the UserCategorizationServiceImpl delegates to a KieSession the responsibility of categorizing the User.

Until here, nothing new right? Same as in my previous post we are using the @KReleaseId to locate the artefact that contains our Rules Definitions. Remember that to get this working, you need to compile the drools-user-kjar project before trying to access this service instance, so KIE-CI can pick it up from the Maven repository.

The only new thing here is how we are going to package, deploy and run our service. As mentioned in the introduction, we are going to use Wildfly Swarm to create a Fat Jar, and for that reason our drools-jax-rs/pom.xml contains the following extra dependencies:

and voila! When we package our project the wildly-swarm-plugin will create a Fat Jar that can be executed.

Interacting with our Service

Now we need to start our service first in order to start sending user to be categorised. Make you that you compile the three projects by executing mvn clean install from the root directory.

Now inside the drools-jax-rs you can go to the target/ directory and you will find the following packaged artifacts:

drools-jax-rs-1.0-SNAPSHOT-swarm.jar

drools-jax-rs-1.0-SNAPSHOT.war

We have two options here, deploy the drools-jax-rs-1.0-SNAPSHOT.war into a wildfly application server or just execute our fat jar drools-jax-rs-1.0-SNAPSHOT-swarm.jar from the terminal. The main approach of having a Fat Jar is to avoid the need of installing and configuring an Application Server. The Fat Jar is self-contained and it only requires some to start it up.

Now your service is up and running and you can start interacting with it.

In order to do that, you can write a JavaScript client, a Java Client, or even an Android Client to call the rest services that we just exposed. Here for testing purposes I’m using Postman, a Google Chrome Extension that allows you to build HTTP requests.

creating a request with Postman

Here you can see that I’m sending a User object (in JSON) using a POST HTTP request to the http://localhost:8080/api/users/categorize endpoint and the service is returning the same object but now with the category field set to “Adult”.

Summary

On this blog post, we saw how to put all the pieces together to get our rules bundled inside our micro-service. For this kind of scenarios we want to abstract the end user for the implementation details of our service, even hiding the underlaying technology that we are using and enabling the client to be written in any programming language that supports the creation of HTTP calls.

We saw how to use Wildfly Swarm to create a self contained package that can be executed without previously installing anything else but Java. In my next post I will go one step further and we will be using Docker to remove the need of installing Java and to make our micro service one step forward to be cloud enabled.

PS: I know that these posts looks a little bit rough and unpolished, so feel free to ask questions if there is something that is not explained in detail.

Is the rules engine using a stateful session or stateless session ? One of my major complaints is that drools doesn’t scale horizontally that well because it doesn’t have the ability to distribute facts across multiple servers easily. Maybe this has changed ?

KieSessions are stateful. Your complaint applies to every rule engine based on the Rete algorithm. Now with the new algorithm called phreak, we are one step closer to distribute the algorithm in multiple nodes. But again this might not be the best solution, due the chattines that will be generated between the nodes to perform the evaluations. In my opinion, one of the strengths of drools is that creating a new instance of the engine is extremely simple, this enables you to split your data and the coordinate sessions if needed. HTH

I’ve been out of hardcore Java dev for about 3 years. I started following these tutorials as I used older versions of Drools and wanted a quick and easy working example of Drools 6. I got that, and had my eyes opened to the world of Weld, Arquillian and WildFire along the way. Things have come a long way. Fantastic job on the series of tutorials, thank you for sharing.

Can you suggest in what format these rules should be generated? Should they be in drl again ? I suppose generation is only one part, I will have to package and deploy too (just like you mentioned here) right ? Is there a way to completely eliminate this deploy step ?

You have two options: 1) generate the rules in DRL which is the execution format 2) Generate your own model to then transform to DRL. Option 2 is a commonly used approach if you are building a very domain specific Rule Authoring UI.

Regarding deployment, you can add your new rules to the Knowledge Base, but that’s dangerous in my opinion, becuase you never know how the newly generated rules will behave in runtime. If you are confident that you can guarrantee that the new rules will be ok, you can do that to avoid the deployment step. Having said that, and with all the new technology that we have available today, such as docker and kubernetes, you should just deploy a new version of your rules service with the newly generated rules. In that case, if something goes wrong you can quickly revert back to the old version.

I would recommend you to start simple, create a set of Rules in DRL until you figure out how your rules will look like. The next step is to make sure that you, your users can parameterize those rules, and you can achive that by just using templates, where instead of actual values in your rules you can have variables that are coming from the user input. Then if that’s not enough you can start thingking about creating your own model to generate DRL, but that all up to you to develop.

I can’t believe the amount of files maven downloaded when I installed the project that goes along with this blog (took more than ten minutes), which really worried me; with some many dependencies, something is bound to go wrong. But the Fat jar worked the very first time! I’m impressed. Thank you! Now I need to rebuild it with my rules and my restful api…. Now the errors will start popping up. Do you have any recommendations for debugging?

Then I put my daughter’s data in (she’s 12), and no rule fired. Checking the rules.drl in the src/main/resources of drools-user-kjar, I discovered that you had made the embarrassing newbie mistake that all software developers make all the time; Line 20 should have been:
$u: User( age = 0,…
Not
$u: User( age = 0,…
Or you can add a tween rule for 11 and 12 year olds.
(You did the range boundary correctly for the adult rule).

Then I changed age to Double (to take into account fractions of years), and added an ArrayList of skills to User.java in drools-user-model:
public class User {
private String name;
private Double age;
private String category;
private ArrayList skills;

With getter and setter, of course. And I used the new variables in a rule:

I must delete and insert the “}” token?
What is the correct way to use ArrayLists in the “then” part of a rule?

Finally, when I look inside my maven repository, I can see my rules inside of drools-user-kjar-1.0.jar. What I don’t understand is that inside the drools-jax-rs-1.0.war file, in the “WEB-INF\lib” directory, I can see drools-user-model-1.0.jar (which contains the User.class file). But that same lib directory, which contains so many other jar files, does not contain drools-user-kjar-1.0.jar. Where are the rules hidden inside the war file?

Lol, newbie mistake… the rules were not the focus of the example, the fat jar was.. it is up to the implementor to check the validity of the rules, don’t rely on examples from the Internet. And by the way doing $u: User( age = 0,…
Not
$u: User( age = 0,…

Is something that I wouldn’t recommend.
As a Java pro you shouldn’t use ArrayList to define variables, you should use the generic interface List.

The problem that you are having is that you are not building your modified user class and including that one inside the war, are you building all the projects with maven? And did you built everything after your changes? HTH

The error should be part of the plugin output.. as far as I remember. Are you still using Eclipse? I think that the plugin is not maintained anymore.. Look in the KIE workbench to see if there a new alternative for the viewer.

Framework? What framework? I’m just running your three Maven projects under drools-rest-swarm:
drools-jax-rs, drools-user-kjar, and drools-user-model.

The problem with using globals is that they are supposed to be constants; not recommended for containing time stamps.

What I ended up doing is, at the beginning of my rules, run a rule that would update the current time in my data object (the “no-loop true” directive was very important!):
rule “Update Market Age”
no-loop true
when
drug : MedicaidDrug ()
then
String msg = “Updating current date and age. “+ drools.getRule().getName()+”: “+drug.toString();
drug.addMessage(msg);
System.out.println(msg);
drug.updateAge();
update(drug)
end

I tried to follow your article with three simple maven projects (model, kjar and jax-rs). I tried to separate rules by adding them in kjar. Only kjar and jax-rs projects has dependency pointed to model project. All projects has beans.xml and kjar has kmodule.xml.

When I tried in that arrangement and tried to run xxx-swarm-xxx.jar, I hit two issues: one is similar to the one below (complaining about KieSession) and another one is complaint that maven artifact of “model” project is not found.

Appreciate any advice.

[org.jboss.weld.Bootstrap] (Weld Thread Pool — 1) WELD-000119: Not generating any bean definitions from because of underlying class loading error: Type org.kie.api.runtime.KieSession from [Module “deployment.-0.0.1.jar:main” from Service Module Loader] not found. If this is unexpected, enable DEBUG logging to see the full error

Hi Aung,
it is very difficult to provide any hint without knowing exactly how your projects look like. You need to check in the swarm package if the model is included there. If it is not found there might be some issues while you declare the dependencies.

To share with you about my finding…..The only difference I see so far between your sample project and my test project is that missing packaging info in pom.xml file. Initially I didn’t specified project’s packaging as “war”. After I specified packaging as “war” in pom.xml file, my custom “drools-jax-rs” project works.

This is what I did. I clone your sample “drools-jax-rs” project and replaced your kjar and model references with my custom kjar and model references while sticking to drools-6.3.0.Final. That worked. So, I upgraded drools to 6.5.0 and it still worked. After that, I compared your sample “drools-jax-rs” project and my test project piece by piece.

Thank you very much for your kind guidance. Have a nice weekend ahead.

Yes, it’s all working. I get docker image with drools service working well.

Now, I’m trying to use Kie Scanner but it’s failing. The main purpose is to get KieScanner scanning kjar artifact (RELEASE version) periodically and get rule automatically updated.

I tried to get Kie Scanner from KieServices which in run was created by KieServices.Factory.get() method. I tried to use injected KieContainer but it failed. I also tried to use KieContainer through KieServices but it somehow is failing too.

Well but how is it failing? Did you configured where the kie scanner is looking for your rules? Remember that it uses maven to do resolutions so it requires a local or remote maven repo to resolve your artifacts

It’s failing at kjar module dependency resolution through kieService object’s newReleasedId method. When I used open-ended version, it worked. When I released new kjar version, Kie Scanner picked it up and worked fine. When I used “RELEASE” version, it complaint about that and resolution failed.

I tried latest drools version – 7.0.0.Final, and 7.1.0.Beta3 but they don’t work well for dependency resolution through “RELEASE” and “LATEST” version label. I need to stick to open-ended version label at the moment, I guess.

I reported my issue through JBoss JIRA board. I believe, maven arrangement is fine because resolution through open-ended version label works well for starting KieContainer instance through base kjar version and also getting new kjar version through my maven repository.

Hi Mauricio,
I implemented an enhanced version of your three maven projects (drools-jax-rs, drools-kjar, and drools-model) under a parent. On my local MS Windows 10 box, the resulting war file works great (running all my rules on my complex data object when I post some JSON to the RESTful end point). But when I place that same war file in the EAP deployment directory on an AWS box running Red Hat Enterprise Linux Server release 6.9 (Santiago), it doesn’t deploy. I’m running EAP 7.0 and Java 1.8.0 on both machines (I wrote a HelloEAP project whose war file works fine on both machines). What could cause such strange behavior? Does Maven/EAP/Drools work differently on Windows and Linux operating systems?

The stack trace on the AWS Linux box:

[root deployments]# more drools-jax-rs-1.0.war.failed
“{\”WFLYCTL0080: Failed services\” => {\”jboss.deployment.unit.\\\”drools-jax-rs-1.0.war\\\”.WeldStartService\” => \”org.jboss.m
sc.service.StartException in service jboss.deployment.unit.\\\”drools-jax-rs-1.0.war\\\”.WeldStartService: Failed to start service
Caused by: org.jboss.weld.exceptions.DefinitionException: Exception List with 1 exceptions:
Exception 0 :
java.lang.RuntimeException: Cannot find KieModule: org.mycompany:drools-kjar:1.0
at org.drools.compiler.kie.builder.impl.KieServicesImpl.newKieContainer(KieServicesImpl.java:117)
at org.drools.compiler.kie.builder.impl.KieServicesImpl.newKieContainer(KieServicesImpl.java:111)
at org.drools.compiler.cdi.KieCDIExtension.afterBeanDiscovery(KieCDIExtension.java:287)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.jboss.weld.injection.StaticMethodInjectionPoint.invoke(StaticMethodInjectionPoint.java:88)
at org.jboss.weld.injection.MethodInvocationStrategy$SpecialParamPlusBeanManagerStrategy.invoke(MethodInvocationStrategy.java:144)
at org.jboss.weld.event.ObserverMethodImpl.sendEvent(ObserverMethodImpl.java:309)
at org.jboss.weld.event.ExtensionObserverMethodImpl.sendEvent(ExtensionObserverMethodImpl.java:124)
at org.jboss.weld.event.ObserverMethodImpl.sendEvent(ObserverMethodImpl.java:287)
at org.jboss.weld.event.ObserverMethodImpl.notify(ObserverMethodImpl.java:265)
at org.jboss.weld.event.ObserverNotifier.notifySyncObservers(ObserverNotifier.java:271)
at org.jboss.weld.event.ObserverNotifier.notify(ObserverNotifier.java:260)
at org.jboss.weld.event.ObserverNotifier.fireEvent(ObserverNotifier.java:154)
at org.jboss.weld.event.ObserverNotifier.fireEvent(ObserverNotifier.java:148)
at org.jboss.weld.bootstrap.events.AbstractContainerEvent.fire(AbstractContainerEvent.java:53)
at org.jboss.weld.bootstrap.events.AbstractDefinitionContainerEvent.fire(AbstractDefinitionContainerEvent.java:42)
at org.jboss.weld.bootstrap.events.AfterBeanDiscoveryImpl.fire(AfterBeanDiscoveryImpl.java:61)
at org.jboss.weld.bootstrap.WeldStartup.deployBeans(WeldStartup.java:422)
at org.jboss.weld.bootstrap.WeldBootstrap.deployBeans(WeldBootstrap.java:83)
at org.jboss.as.weld.WeldStartService.start(WeldStartService.java:95)
at org.jboss.msc.service.ServiceControllerImpl$StartTask.startService(ServiceControllerImpl.java:1948)
at org.jboss.msc.service.ServiceControllerImpl$StartTask.run(ServiceControllerImpl.java:1881)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at java.lang.Thread.run(Thread.java:745)
\”}}”

No idea about EAP that’s a paid product, in this blog
Only open source stuff 😉 . The key of the error is in : KieModule: org.mycompany:drools-kjar:1.0 . It might aether trying to resolve that from the local maven repo and in AWS you don’t have a local maven repo , that sounds like the probable cause of the issue. HTH

Hi Mauricio,
Thanks for great Article, i need little guidance, i am new to Drool, i want to Distribute rule repo created by Drool Workbench in Docker container, I found that i need to use KIE-Server for that, but i am not able to find proper resources for that.

To rephrase my requirement again, i want to expose rule as REST endpoint using Docker Container.

Hi there, yes the KIE Server is definitely the way to go, to avoid you creating the rest endpoints. But I would suggest you if you are new to Drools, to first create a simple Java / Spring application to test your rules. Are you at that point already? Can you run your rules and test them? When you use the KIE Server, the rules will be executed remotely, so you need to understand how Drools behaves before going to a remote server. Have you looked at the docs: https://docs.jboss.org/drools/release/7.11.0.Final/drools-docs/html_single/index.html#_ch.kie.server ?

If you wanted to change this from stateful to stateless what change would be necessary, if it just minor configuration or something more significant.
Also is this multi-threaded, if it get multiple requests at the same time will they get processed or would that require additional code or a different approach.

In Drools, Stateless sessions are just wrappers of Stateful sessions that can be quickly disposed. Drools will deal with multiple requests at the same time if you are using Drools 7+, but check the docs or write to the IRC channels to get more details about that.

I tried to build this with the current version of Drools 7.14.0.Final, it built the jar and the war ok. But when I ran the swarm-jar I got the following
2018-11-16 15:59:01,378 ERROR [org.jboss.msc.service.fail] (MSC service thread 1-6) MSC000001: Failed to start service jboss.deployment.unit.”drools-jax-rs-1.0-SNAPSHOT.war”.WeldStartService: org.jboss.msc.service.StartException in service jboss.deployment.unit.”drools-jax-rs-1.0-SNAPSHOT.war”.WeldStartService: Failed to start service
at org.jboss.msc.service.ServiceControllerImpl$StartTask.run(ServiceControllerImpl.java:1904)
at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
at java.lang.Thread.run(Unknown Source)
Caused by: org.jboss.weld.exceptions.DeploymentException: WELD-001408: Unsatisfied dependencies for type KieSession with qualifiers @KReleaseId @KSession
at injection point [BackedAnnotatedField] @Inject @KReleaseId @KSession private org.drools.workshop.endpoint.impl.UserCategorizationServiceImpl.kSession ……

Is there anything obvious that needs to be updated to get this example to run with the current version of Drools.

This blog post is from 3 years ago, so tons of changes might have happened. The error that you see is that there is a missing jar in the classpath, the one that implements KieSession. Check the drools docs and verify the dependencies