如何让 Drools 访问动态加载的类?

如何解决如何让 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.newKieBuilderKieServices.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 举报,一经查实,本站将立刻删除。

相关推荐


Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其他元素将获得点击?
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。)
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbcDriver发生异常。为什么?
这是用Java进行XML解析的最佳库。
Java的PriorityQueue的内置迭代器不会以任何特定顺序遍历数据结构。为什么?
如何在Java中聆听按键时移动图像。
Java“Program to an interface”。这是什么意思?
Java在半透明框架/面板/组件上重新绘画。
Java“ Class.forName()”和“ Class.forName()。newInstance()”之间有什么区别?
在此环境中不提供编译器。也许是在JRE而不是JDK上运行?
Java用相同的方法在一个类中实现两个接口。哪种接口方法被覆盖?
Java 什么是Runtime.getRuntime()。totalMemory()和freeMemory()?
java.library.path中的java.lang.UnsatisfiedLinkError否*****。dll
JavaFX“位置是必需的。” 即使在同一包装中
Java 导入两个具有相同名称的类。怎么处理?
Java 是否应该在HttpServletResponse.getOutputStream()/。getWriter()上调用.close()?
Java RegEx元字符(。)和普通点?