[
https://issues.jboss.org/browse/JBRULES-3669?page=com.atlassian.jira.plug...
]
Davide Sottara commented on JBRULES-3669:
-----------------------------------------
This might be a critical bug, or not a bug at all, I'd like to hear other opinions...
The issue is that you are building the knowledege base incrementally. The second knowledge
builder, then, needs to share the same classloader as the first. To do this, you
correctly
pass the KnowledgeBase to the second KnowledgeBuilder. Now, this would be enough:
kbuilderRules will apply the changes directly into the existing knowledgebase. In this
case, there is
no need to create a second knowledge base.
If you wanted to create a second knowledge base, you will have to share the first
knowledge base's classloader:
KnowledgeBaseConfiguration kbConf = KnowledgeBaseFactory.newKnowledgeBaseConfiguration(
null,
((ReteooRuleBase)((KnowledgeBaseImpl)
kbaseModel).getRuleBase()).getRootClassLoader() );
KnowledgeBase kbaseRules = KnowledgeBaseFactory.newKnowledgeBase( kbConf );
I agree that this sequence of casts is not immediate, but the knowledge base configuration
and the package building process is being rewritten in 6.0 anyway.
Also consider that the KnowledgeAgent is (supposedly) the recommended way of managing
incremental KBs in 5.x
Best
Davide
Oh, btw, you are missing a ";" in the rule, but this might be a cut&paste
typo :)
Bug when using two knowledge bases and one declarative model between
--------------------------------------------------------------------
Key: JBRULES-3669
URL:
https://issues.jboss.org/browse/JBRULES-3669
Project: Drools
Issue Type: Bug
Security Level: Public(Everyone can see)
Components: drools-compiler
Affects Versions: 5.4.0.Final
Reporter: Jackson Cunha
Assignee: Mark Proctor
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 packageDescr and 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 kbaseModel and 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);
--
This message is automatically generated by JIRA.
If you think it was sent incorrectly, please contact your JIRA administrators
For more information on JIRA, see:
http://www.atlassian.com/software/jira