如何解决如何让 Drools 访问动态加载的类?
我正在尝试使用 ClassLoader 在运行时从 .class 文件加载类,并在 Drools 规则 (Drools 7.52.0) 中使用它们。我正在使用这个自定义 ClassLoader,它从文件中读取并使用 ClassLoader.defineClass()
加载一个类。它类似于 urlclassloader:
package example;
import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.IOException;
public class DynamicclassLoader extends ClassLoader {
public DynamicclassLoader(ClassLoader parent) {
super(parent);
}
/**
* Define a class from a .class file and return it
* @param filepath path to a .class file
* @return new Class or null if error
*/
public Class<?> classFromFile(String filepath) {
try {
// Read .class file and write bytes to buffer
BufferedInputStream input = new BufferedInputStream(new FileInputStream(filepath));
// Write file contents to buffer
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
int data = input.read();
while(data != -1){
buffer.write(data);
data = input.read();
}
byte[] classData = buffer.toByteArray(); // contents of .class file
input.close();
return defineClass(null,classData,classData.length);
} catch (IOException | ClassFormatError e) {
e.printstacktrace();
}
return null;
}
}
我可以使用 ClassLoader 加载一个类、构造一个实例并访问它的方法。但是,即使我通过方法 KieServices.newKieBuilder
和 KieServices.newKieContainer
将 ClassLoader 传递给 Drools,Drools 也无法编译规则。这是我的 Main.java:
package example;
import java.io.IOException;
import org.kie.api.builder.model.KieBaseModel;
import org.kie.api.builder.model.KieModuleModel;
import org.kie.api.runtime.KieSession;
import org.kie.api.KieServices;
import org.kie.api.builder.KieBuilder;
import org.kie.api.builder.KieFileSystem;
import org.kie.api.builder.Message;
public class Main {
private static KieServices kServices = KieServices.Factory.get();
private static KieFileSystem kFileSys;
private static KieSession kSession;
private static DynamicclassLoader classLoader = new DynamicclassLoader(Main.class.getClassLoader());
public static void main(String[] args) throws IOException,ClassNotFoundException {
// Load Person class
classLoader.classFromFile("Person.class");
// Instantiate Person
Class<?> personClass = classLoader.loadClass("facts.Person");
Object person = null;
try {
// Instantiate person and access methods using reflection
person = personClass.getConstructor(String.class,Integer.class).newInstance("Alice",49);
System.out.println(person.getClass() + ": name = " +
(String) personClass.getmethod("getName").invoke(person));
} catch (Exception e) {
System.out.println("Error instantiating person");
e.printstacktrace();
}
// Create a KieSession with a rule that uses the Person class
String drl = String.join("\n","import facts.Person;","rule \"person\""," when"," $p : Person()"," then"," System.out.println($p.getName());","end"
);
initializeKieSession(drl);
kSession.insert(person);
kSession.fireAllRules();
}
/**
* Create a KieSession using the given ruleset
* @param drl a ruleset string in Drools Rule Language
*/
private static void initializeKieSession(String drl) {
// Create module model
KieModuleModel kModMod = kServices.newKieModuleModel();
KieBaseModel kBaseMod = kModMod.newKieBaseModel("KBase_std").setDefault(true);
kBaseMod.newKieSessionModel("KSession_std").setDefault(true);
// Create file system with module model
kFileSys = kServices.newKieFileSystem();
kFileSys.writeKModuleXML(kModMod.toXML());
// Write rules
kFileSys.write("src/main/resources/person.drl",drl);
KieBuilder kBuilder = kServices.newKieBuilder(kFileSys,classLoader).buildAll();
boolean errors = kBuilder.getResults().hasMessages(Message.Level.ERROR);
if (errors) {
for (Message message : kBuilder.getResults().getMessages())
System.out.println(message.getText());
}
// new KieSession
kSession = kServices.newKieContainer(
kServices.getRepository().getDefaultReleaseId(),classLoader).getKieBase().newKieSession();
}
}
编译此规则(使用 KieBuilder.buildAll()
)给出错误
Rule Compilation error Only a type can be imported. facts.Person resolves to a package. facts.Person cannot be resolved to a type.
如果我不将 ClassLoader 传递给 KieBuilder,我会收到另外两个错误:
Unable to resolve ObjectType 'Person'. $p cannot be resolved.
所以我的 ClassLoader 正在做一些事情,但没有让 Drools 完全访问它加载的任何类。我怎样才能解决这个问题?我在这个问题上花了几天时间,但找不到任何有用的东西。
解决方法
看起来任何加载的类文件的内容也需要在编译之前写入 KieFileSystem。
所以要让 Drools 完全访问一个类,需要以下内容:
ClassLoader classLoader;
// Use classLoader to load external classes
...
// Copy class definitions to the KieFileSystem
for (/*each loaded class*/) {
String filename = packageName + '/' + className + ".class";
kFileSys.write(filename,byteArrayOfClassFileContents);
}
// Pass classLoader to both newKieBuilder and newKieContainer
KieServices kServices = KieServices.Factory.get();
KieBuilder kBuilder = kServices.newKieBuilder(kFileSys,classLoader).buildAll();
KieContainer kContainer = kServices.newKieContainer(
kServices.getRepository().getDefaultReleaseId(),classLoader);
请注意,在 KieFileSystem 根目录下的包中编写类文件很重要。例如,类 foo.bar.Baz
的定义应写入 "foo/bar/Baz.class"
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。