Hello conrads.
To explain what I need, the problem that I found and the solution that I
planning to use, I will firstly describe my cenario.
*Cenario*
In my cenario, I have a declarative model and rules of different groups. I
need put each rules group in a different knowledge base and all of it will
share the same declarative model.
In a time, I will create a fact using fact types defined in a knowledge
base that I will call as kbaseModel. This knowledge base will have only the
declarative model and no rules.
All facts created will be used in other knowledge base, called kbaseRules.
All kbaseRules have only rules and no declarative model, because model was
defined in kbaseModel.
I could have many kbaseRules but exists only one kbaseModel.
Using the Drools API, teoricaly, I could implement my cenario using the
following code:
//DRL for resourceModel
declare MyEntity
name : String
end
//DRL for resourceRules
rule "Simple Rule"
when
$entity: MyEntity()
then
System.out.println($entity)
end
Resource resourceModel = ... //Resource with only the declarative model and
no rules
Resource resourceRules = ... //Resource with only rules and no declarative
model
KnowledgeBuilder kbuilderModel =
KnowledgeBuilderFactory.newKnowledgeBuilder();
kbuilderModel add(resourceModel, ResourceType.DRL);
KnowledgeBase kbaseModel = kbuilderModel.newKnowledgeBase();
KnowledgeBuilder kbuilderRules =
KnowledgeBuilderFactory.newKnowledgeBuilder(kbaseModel);
kbuilderRules.add(resourceRules, ResourceType.DRL);
KnowledgeBase kbaseRules = KnowledgeBaseFactory.newKnowledgeBase();
kbaseRules.addKnowledgePackages(kbuilderRules.getKnowledgePackages());
FactType factType = kbaseModel.getFactType("mymodel", "MyEntity");
Object myEntity = factType.newInstance();
factType.set(myEntity, "name", "Jackson Cunha Cassimiro");
StatelessKnowledgeSession session =
kbaseRules.newStatelessKnowledgeSession();
session.execute(myEntity);
This code should work, but when I try to run I get this stacktrace:
java.lang.ClassNotFoundException: Unable to load class: mymodel.MyEntity
at
org.drools.util.CompositeClassLoader.loadClass(CompositeClassLoader.java:92)
at
org.drools.common.AbstractRuleBase.registerAndLoadTypeDefinition(AbstractRuleBase.java:649)
at
org.drools.common.AbstractRuleBase.addPackages(AbstractRuleBase.java:553)
at org.drools.reteoo.ReteooRuleBase.addPackages(ReteooRuleBase.java:472)
at
org.drools.impl.KnowledgeBaseImpl.addKnowledgePackages(KnowledgeBaseImpl.java:150)
at app.UsingDeclares.main(UsingDeclares.java:79)
Exception in thread "main" org.drools.RuntimeDroolsException: unable to
resolve Type Declaration class 'mymodel.MyEntity'
at
org.drools.common.AbstractRuleBase.addPackages(AbstractRuleBase.java:582)
at org.drools.reteoo.ReteooRuleBase.addPackages(ReteooRuleBase.java:472)
at
org.drools.impl.KnowledgeBaseImpl.addKnowledgePackages(KnowledgeBaseImpl.java:150)
at app.UsingDeclares.main(UsingDeclares.java:79)
It happens because Drools can't find any reference of mymodel.MyEntity in
kbaseRules, but this kbase was built from kbaseModel that already have this
class declared. *I think it can be a **bug*.
*Debuging*
During creation of kbaseModel, a method called mergePackage(pkgRegistry,
packageDescr) from PackageBuilder class is invoked. The pkgRegistry not
contains any reference to declarative model. The packageDescr contains a
list of type definitions from declarative model. Inside mergePackage is
invoked other method, called processTypeDeclarations(pkgRegistry,
packageDescr) that will iterate over all type definitions from
packageDescrand will fill a map called Map<String,byte[]>
classLookups from JavaDialectRuntimeData class.
During the creation of kbaseRules the method mergePackage(pkgRegistry,
packageDescr) from PackageBuilder is invoked again, but at this time
pkgRegistry contains all type definitions defined previously in kbaseModeland
packageDescr not contains any type definitions, only rules. When the
method processTypeDeclarations(pkgRegistry,
packageDescr) from PackageBuilder is called and try iterate over type
definitions of packageDescr, this is empty and nothing is done and the
map Map<String,byte[]>
classLookups from JavaDialectRuntimeData will be empty too.
*The classLookups empty is the cause of ClassNotFoundException*.
*Solution*
The solution that I found was apply reflection to fill Map<String,byte[]>
classLookups from JavaDialectRuntimeData correctly before add the packages
into kbaseRules:
Collection<KnowledgePackage> packages =
kbuilderRules.getKnowledgePackages();
Mirror mirror = new Mirror();//Reflection utilities
for (KnowledgePackage kPackage : packages) {
Package pkg = (Package) mirror.on(kPackage).get().field("pkg");
JavaDialectRuntimeData dialect = (JavaDialectRuntimeData)
pkg.getDialectRuntimeRegistry().getDialectData( "java" );
for (FactType factType : kPackage.getFactTypes()) {
Class<?> c = factType.getFactClass();
String className = c.getName();
String classAsPath = className.replace('.', '/') +
".class";
InputStream stream =
c.getClassLoader().getResourceAsStream(classAsPath);
byte[] bytes = IOUtils.toByteArray(stream);
dialect.putClassDefinition(classAsPath, bytes);
}
}
KnowledgeBase kbaseRules = KnowledgeBaseFactory.newKnowledgeBase();
kbaseRules.addKnowledgePackages(packages);
----------------------------------------------------------------------------------------------------------------
Jackson Cunha Cassimiro (CereB)
Bacharel em Ciencia da Computação - UFPI
MSN: jackson.cereb(a)gmail.com
Telefone Móvel +55 86 9928 1251
Analista de Sistemas - Infoway -
http://www.infoway-pi.com.br
Missão Infoway - "Influenciar a Gestão de Sistemas de Saúde através de
e-health"
("A vida é um combate que os fracos abate, aos bravos, aos fortes só pode
exaltar" - Canção do Tamoio, Gonçalves Dias)
----------------------------------------------------------------------------------------------------------------