Now it’s time to jump to a more interesting environment, the Remote Environment.
During this post I will try to show the components that will interact in a remote environment. We will continue using the Drools Grid Services APIs but we will change the underlying implementation from LocalProviders to RemoteProviders.
Remote Providers
We will continue using the previous examples (Local Providers from the previous post) and we will only change the GridTopology configuration.
GridTopologyConfiguration gridTopologyConfiguration =
new GridTopologyConfiguration("MyTopology");
gridTopologyConfiguration.addExecutionEnvironment(
new ExecutionEnvironmentConfiguration(
"MyMinaEnv",
new MinaProvider("127.0.0.1",9123)));
gridTopologyConfiguration.addDirectoryInstance(
new DirectoryInstanceConfiguration(
"MyMinaDir",
new MinaProvider("127.0.0.1",9124)));
return gridTopologyConfiguration;
As you can see the only thing that change are the providers for both the ExecutionEnvironment and the DirectoryInstance.
MinaProvider is a Remote Provider that uses Apache Mina for transport. This Remote Provider gives us the possibility to create, host and execute our knowledge in remote locations.
Characteristics of Remote Executions
As you may know, when you are dealing with Remote Environments you need to have some considerations to be able to distribute your knowledge and some considerations to execute and interact with the remote sessions.
1) All your interactions will go to the wire in different ways depending the chosen underlying implementation. So you need to be careful with the information that you send over the wire. Basically you will need to implement “Serializable” or some mechanism to share information between your apps and the remote provider. You will see in the demo project that all the domain classes has been marked as Serializable.
2) Your knowledge will be executed remotely and your rules should be designed with that restriction in mind. If the rules call a service, this service must be also accessible from the RemoteProvider location.
3) You must distribute your domain model with your rules. This is pretty related with 2. The environment that will actually execute the rules will need to have all the classes loaded in the server classloader to be able to compile and run the rules/knowledge.
Following these considerations for every environment (Local/Remote/Distributed) will let you to switch from one to the other without any problem.
Setting Up the Environment
Once you mark the domain classes to be Serializable and check that your rules can be executed in a remote environment you need to set up the living topology.
As we describe in the GridTopologyConfiguration we have two servers one listening in 127.0.0.1:9123 (localhost) and the other in 127.0.0.1:9124.
For that reason we need to start up both servers. I can simulate the servers in a test scenario, but for practical reasons I prefer to show how to distribute and run each server instance.
The first step is to download both servers: The Execution Environment Server: drools-grid-remote-mina-distro.zip and the Directory Instance server: drools-grid-remote-dir-mina-distro.zip.
You need to unzip the content from both files in different directories. Inside each of them you will find the start.sh script. Running this script (in unix machines, I’m working in bat files for window$) will start each of the servers.
The script contains the information about the host and the ports (I will work on customization for these parameters later).
When you uncompress these files you will see that there is a lib directory. Inside this lib directory you will find all the dependencies required to run the execution node.
One important thing to understand here is that you will probably need to include all the rules dependencies here in the lib directory. So as a second step, you will need to clean and build the drools-grid-mini-demo project and copy the jar to the Execution Environment server lib’s directory(drools-grid-remote-mina/lib/). You will need to have the domain classes (and all the required dependencies) that are being used inside the rules inside the Execution Environment lib directory in order to execute the rules/knowledge remotely.
The third step is to start both the Execution Environment and the Directory Instance Server. You can do that running the ./start.sh script in the console. (take a look inside the script. If you want to change the host or the port where
the server is started).
Note: Usually you wont need to copy dependencies to the Directory Instance lib project.
Analyzing the Remote Topology Knowledge Execution
As you can see in the previous figure, three steps are executed by the application. Using Drools Grid Services, the application queries the available directories to find a running execution environment. The Directory Instance return the registered Execution Environment (Remote) to the application to start working. Once we get an Execution Environment we can create a Knowledge Builder, a Knowledge Base and a Knowledge Session. After we have the session we can start inserting facts and firing all the activated rules.
From the application perspective will be transparent where the rules are executed, but in the background you can check that the current execution is happening in the remote server. The example project that you can download, uses a rule that only prints out the the console that the rule was activated. In this case and just to show that the execution happens in the remote server, you can see that the output is being printed in the server console. In real scenarios you will probably want to send a response or some results to the client application. We will see how to achieve that in following posts.
In Brief
During this post we have seen how we can move our application that was constructed using Local Providers to Remote Providers. You can download a new version of the drools-grid-mini-demo v2 here. You will find a new class to execute called: RemoteTopologyTest that contains the GridTopologyConfiguration that uses the RemoteProviders.
It would be nice to pick the old project from the previous post, download it and then try to migrate to use the Remote Providers. Try to extend the example and let me know if you have any problems.
The next post will be about Distributed Environment, and there is when this stuff begins to make really sense.

I will attempt to test it this weekend.
Points.
1) This framework only see Java Objects. These objects must be swizzled in from the ‘real world’. Assume the ‘real-world’ will never fit in any memory (distributed or otherwise).
2) We are dealing with subsets of external data. These subsets are most performant with high locality.
3) We have Rules Ri,Rj: Ri(A,B,C,S) -> K = f(A,B,C); insert(K)
Rj(D,E,F,S) -> L = f(D,E,F); insert(L)
with S being search space state S
4) If we have Nodes N1, N2 and Size(tuples member (A,B,C,D,E,F)) >> Intersect( {A,B,C}, {D,E,F} ) then it makes sense to have i = A,B,C and j = D,E,F, and N1=i, N2=j
5) If K, L are facts in a search space, which may be retracted, then these state constraints S will be accumulated in memory.
6) Something like Rk(K,S) -> retract K, rollback all associated state from Ri until Rk,update S to avoid K
Until I understand how the tuples are shared amongst nodes, I am probably just muddying the waters…
[...] .Salaboy .open.source.Knowledge! Disclaimer!About Me!Contact Me! « Drools Grid (version 2) – #3 Drools Grid Remote Services [...]
Hi, I am able to run the example successfully and noted that the rules are executed at remote server. I have cluster setup with two JVMs and I am want to use the stream mode (drools events with @expire header). To achieve this generally we use following code if we use single instance:
final KnowledgeBaseConfiguration kbConfig = KnowledgeBaseFactory.newKnowledgeBaseConfiguration();
kbConfig.setOption(EventProcessingOption.STREAM);
KnowledgeBase kbase = KnowledgeBaseFactory.newKnowledgeBase(kbConfig);
Please can you let me know how to achieve this in Grid environment?
Thanks,
Mrunali
Hi,
Is stream mode supported in GRID environment?
I modified the code like:
final KnowledgeBaseConfiguration kbConfig = KnowledgeBaseFactory.newKnowledgeBaseConfiguration();
kbConfig.setOption(EventProcessingOption.STREAM);
KnowledgeBase kbase = node.get(KnowledgeBaseFactoryService.class).newKnowledgeBase(“simpleValidationKbase”,kbConfig);
kbase.addKnowledgePackages(kbuilder.getKnowledgePackages());
StatefulKnowledgeSession ksession = kbase.newStatefulKnowledgeSession();
But when you try to insert the fact after expiration time we get following error:
org.drools.RuntimeDroolsException: Unexpected exception executing action org.drools.reteoo.ReteooWorkingMemory$WorkingMemoryReteExpireAction@1abdac9
at org.drools.common.AbstractWorkingMemory.executeQueuedActions(AbstractWorkingMemory.java:1473)
at org.drools.common.AbstractWorkingMemory.insert(AbstractWorkingMemory.java:1159)
at org.drools.common.AbstractWorkingMemory.insert(AbstractWorkingMemory.java:1123)
at org.drools.common.AbstractWorkingMemory.insert(AbstractWorkingMemory.java:917)
at org.drools.impl.StatefulKnowledgeSessionImpl.insert(StatefulKnowledgeSessionImpl.java:251)
at org.drools.command.runtime.rule.InsertObjectCommand.execute(InsertObjectCommand.java:83)
at org.drools.command.runtime.rule.InsertObjectCommand.execute(InsertObjectCommand.java:38)
at org.drools.command.KnowledgeContextResolveFromContextCommand.execute(KnowledgeContextResolveFromContextCommand.java:66)
at org.drools.grid.internal.GenericMessageHandlerImpl.messageReceived(GenericMessageHandlerImpl.java:52)
at org.drools.grid.remote.mina.ClientGenericMessageReceiverImpl.messageReceived(ClientGenericMessageReceiverImpl.java:67)
at org.drools.grid.remote.mina.MinaIoHandler.messageReceived(MinaIoHandler.java:50)
at org.apache.mina.core.filterchain.DefaultIoFilterChain$TailFilter.messageReceived(DefaultIoFilterChain.java:713)
at org.apache.mina.core.filterchain.DefaultIoFilterChain.callNextMessageReceived(DefaultIoFilterChain.java:434)
at org.apache.mina.core.filterchain.DefaultIoFilterChain.access$1200(DefaultIoFilterChain.java:46)
at org.apache.mina.core.filterchain.DefaultIoFilterChain$EntryImpl$1.messageReceived(DefaultIoFilterChain.java:793)
at org.apache.mina.filter.codec.ProtocolCodecFilter$ProtocolDecoderOutputImpl.flush(ProtocolCodecFilter.java:375)
at org.apache.mina.filter.codec.ProtocolCodecFilter.messageReceived(ProtocolCodecFilter.java:229)
at org.apache.mina.core.filterchain.DefaultIoFilterChain.callNextMessageReceived(DefaultIoFilterChain.java:434)
at org.apache.mina.core.filterchain.DefaultIoFilterChain.access$1200(DefaultIoFilterChain.java:46)
at org.apache.mina.core.filterchain.DefaultIoFilterChain$EntryImpl$1.messageReceived(DefaultIoFilterChain.java:793)
at org.apache.mina.filter.logging.LoggingFilter.messageReceived(LoggingFilter.java:176)
at org.apache.mina.core.filterchain.DefaultIoFilterChain.callNextMessageReceived(DefaultIoFilterChain.java:434)
at org.apache.mina.core.filterchain.DefaultIoFilterChain.access$1200(DefaultIoFilterChain.java:46)
at org.apache.mina.core.filterchain.DefaultIoFilterChain$EntryImpl$1.messageReceived(DefaultIoFilterChain.java:793)
at org.apache.mina.core.filterchain.IoFilterAdapter.messageReceived(IoFilterAdapter.java:119)
at org.apache.mina.core.filterchain.DefaultIoFilterChain.callNextMessageReceived(DefaultIoFilterChain.java:434)
at org.apache.mina.core.filterchain.DefaultIoFilterChain.fireMessageReceived(DefaultIoFilterChain.java:426)
at org.apache.mina.core.polling.AbstractPollingIoProcessor.read(AbstractPollingIoProcessor.java:638)
at org.apache.mina.core.polling.AbstractPollingIoProcessor.process(AbstractPollingIoProcessor.java:598)
at org.apache.mina.core.polling.AbstractPollingIoProcessor.process(AbstractPollingIoProcessor.java:587)
at org.apache.mina.core.polling.AbstractPollingIoProcessor.access$400(AbstractPollingIoProcessor.java:61)
at org.apache.mina.core.polling.AbstractPollingIoProcessor$Processor.run(AbstractPollingIoProcessor.java:969)
at org.apache.mina.util.NamePreservingRunnable.run(NamePreservingRunnable.java:64)
at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:651)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:676)
at java.lang.Thread.run(Thread.java:595)
Caused by: java.lang.UnsupportedOperationException: This method is not supported for disconnected objects
at org.drools.common.DisconnectedWorkingMemoryEntryPoint.retract(DisconnectedWorkingMemoryEntryPoint.java:55)
at org.drools.reteoo.ReteooWorkingMemory$WorkingMemoryReteExpireAction.execute(ReteooWorkingMemory.java:419)
at org.drools.common.AbstractWorkingMemory.executeQueuedActions(AbstractWorkingMemory.java:1471)
… 35 more
I’m not sure what you mean with:
“But when you try to insert the fact after expiration time we get following error:”
What are you executing exactly?
Can you dig a little bit to find which method is throwing this?
“Caused by: java.lang.UnsupportedOperationException: This method is not supported for disconnected objects”
Is stream mode supported in remote GRID environment?
I have modified drl like:
declare Person
@role(event)
@expires(2m)
end
Then in the code:
Person p1 = new Person(“”,27,Gender.MALE,null);
ksession.insert(p1);
ksession.fireAllRules();
Person p2 = new Person(“”,28,Gender.MALE,null);
ksession.insert(p2);
ksession.fireAllRules();
//Sleep for 2 min
Thread.currentThread().sleep(120000);
After that if I execute
ksession.fireAllRules();
I get the above mentioned error.
That is once the face is detracted (after expiry time) I am getting this error.
Hope this clarifies your question.
-Mrunali
Yes, it is but it seems to be a problem with retraction. It seems that the retract command is complaining about disconnected fact handles. In order to achieve remoting we need to disconnect the fact handle, so probably some tuning is necessary in the remoting implementation.
Hi, Can we except possible fix for this?
-Mrunali
Hi, Also I found that if we update the domain object in then part of the rule (set the person name in this example), after firing the rules in client application, the person object is not modified. That is name is not set for person object. However this will work in case of single JVM instance. Is this expected behavior or I am missing something here?
If you don’t share examples is impossible to see what is going wrong.
How do you know that the object is not modified? How are you testing that? How your rule looks like?
I have used the same example as attached to post (drools-grid-mini-demo v2) with little modifications.
1. Drl file is modified like below:
————————————————
package com.wordpress.salaboy.rules;
import com.wordpress.salaboy.domain.Person
import java.util.List
declare Person
@role(event)
@expires(2m)
end
rule “Simple validation rule1″
when
p: Person(name == “”)
list : List() from collect( Person(name == “”) )
then
System.out.println(“1>>>>> ALERT !!! >>>>> The Person name should contain a value” );
System.out.println(“2>>>>> ALERT !!! >>>>> Number persons withoud name is: ” + list.size());
p.setName(“XYZ”);
System.out.println(“3″);
end
————————————————
2. RemoteTopologyTest is modified as below:
a. initializeAndRegisterSession() method changes to use Stream Mode KnowledgeBase
final KnowledgeBaseConfiguration kbConfig = KnowledgeBaseFactory.newKnowledgeBaseConfiguration();
kbConfig.setOption(EventProcessingOption.STREAM);
KnowledgeBase kbase = node.get(KnowledgeBaseFactoryService.class).newKnowledgeBase(“simpleValidationKbase”,kbConfig);
kbase.addKnowledgePackages(kbuilder.getKnowledgePackages());
StatefulKnowledgeSession ksession = kbase.newStatefulKnowledgeSession();
b. After ksession.fireAllRules() in main method print user name
Person p1 = new Person(“”,27,Gender.MALE,null);
ksession.insert(p1);
ksession.fireAllRules();
System.err.println(“Person’s name is: ” + p1.getName());
Let me know if you need more details. I can send the complete source code
Ok so let’s start simple:
“when
p: Person(name == “”)
list : List() from collect( Person(name == “”) )”
Are you sure to understand what that rule is doing? Because for me that doesn’t make any sense.
If in the then part you are changing the person object,if you are working in two different JVMs you will end up with two instances of the Person object, do you understand why right?
Then using Person as an event is logically wrong as well.
So I guess that you need to start with simple rules in a single JVM and then move to an environment where remote interactions takes place, because in order to get that working you need to gain a more deep understanding of what is going on.
Cheers
No fix will be introduced unless you report a jira with a coherent example that demonstrate a real issue. You have the source code as well, so you can implement the fixes and send them if you want to, thats the great thing about open source
Cheers
Hi,
Thanks for your inputs. I have following requirement:
We have cluster setup with two JVMs and we have rules in stream mode.
As we discussed, this (Drools grid remote service) seem to have issue with retracting objects from working memory after specified expiry time. Can you suggest possible fix for the same or any other alternate approach which we can use for application running in cluster mode.
Thanks,
Mrunali
I’ve already suggested you two approaches:
1) try with the latest version of grid
2) download the code and check the method that wasn’t implemented
Cheers
Hi,
Can Drools persistence context be a workaround here?
Thanks
Ravi
Not sure about what you mean with that question.. can you elaborate more?