0

我正在编写一个简单的实用程序,它读取 XML 文件,将它们的节点转换为 POJO,将它们加载到 Drools 的 WM 中,最后对它们应用一些规则。您可以在我的GitHub 个人资料上找到整个项目。不幸的是,尽管我付出了所有努力,我还是无法让 Drools “喜欢”在运行时编译的任何类实例。我看到很多人在使用 ClassLoader 时也遇到了问题,所以我怀疑这可能是它的错……我准备了一个最小的工作示例供您尝试,它可以在GitHub和下面的此处获得。它需要一些仅在GitHub 上MemoryFileManager可用的其他小文件(MemoryJavaClassObject和)MemoryJavaFileObject为简洁起见。为了正常工作,此示例要求您的 JVM 是 JDK >= 6,并且您tools.jar的. 示例如下:classes.jarclasspath

public class Example {

    /**
     * @param args
     */
    public static void main(String[] args) {
        // Setting the strings that we are going to use...
        String name = "Person";
        String content = "public class " + name + " {\n";
        content += "    private String name;\n";
        content += "    public Person() {\n";
        content += "    }\n";
        content += "    public Person(String name) {\n";
        content += "        this.name = name;\n";
        content += "    }\n";
        content += "    public String getName() {\n";
        content += "        return name;\n";
        content += "    }\n";
        content += "    public void setName(String name) {\n";
        content += "        this.name = name;\n";
        content += "    }\n";
        content += "    @Override\n";
        content += "    public String toString() {\n";
        content += "        return \"Hello, \" + name + \"!\";\n";
        content += "    }\n";
        content += "}\n";
        String value = "HAL";
        String rules = "rule \"Alive\"\n";
        rules += "when\n";
        rules += "then\n";
        rules += "    System.out.println(\"I'm alive!\")\n";
        rules += "end\n";
        rules += "\n";
        rules += "rule \"Print\"\n";
        rules += "when\n";
        rules += "    $o: Object()\n";
        rules += "then\n";
        rules += "    System.out.println(\"DRL> \" + $o.toString())\n";
        rules += "end\n";

        // Compiling the given class in memory
        JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
        JavaFileManager manager = new MemoryFileManager(compiler.getStandardFileManager(null, null, null));
        ClassLoader classLoader = manager.getClassLoader(null);
        List<JavaFileObject> files = new ArrayList<JavaFileObject>();
        files.add(new MemoryJavaFileObject(name, content));
        compiler.getTask(null, manager, null, null, null, files).call();

        try {
            // Instantiate and set the new class
            Class<?> person = classLoader.loadClass(name);
            Method method = person.getMethod("setName", String.class);
            Object instance = person.newInstance();
            method.invoke(instance, value);
            System.out.println(instance);
            System.out.println("We get a salutation, so Person is now a compiled class in memory loaded by the given ClassLoader.");

            // Use the same instance in Drools (by means of the shared ClassLoader)
            KnowledgeBuilderConfiguration config1 = KnowledgeBuilderFactory.newKnowledgeBuilderConfiguration(null, classLoader);
            KnowledgeBuilder builder = KnowledgeBuilderFactory.newKnowledgeBuilder(config1);
            builder.add(ResourceFactory.newByteArrayResource(rules.getBytes()), ResourceType.DRL);
            if (builder.hasErrors()) {
                for (KnowledgeBuilderError error : builder.getErrors())
                    System.out.println(error.toString());
                System.exit(-1);
            }
            KnowledgeBaseConfiguration config2 = KnowledgeBaseFactory.newKnowledgeBaseConfiguration(null, classLoader);
            KnowledgeBase base = KnowledgeBaseFactory.newKnowledgeBase(config2);
            base.addKnowledgePackages(builder.getKnowledgePackages());
            StatefulKnowledgeSession session = base.newStatefulKnowledgeSession();
            session.insert(instance);
            session.fireAllRules();
        } catch (ClassNotFoundException e) {
            System.out.println("Class not found!");
        } catch (IllegalAccessException e) {
            System.out.println("Illegal access!");
        } catch (InstantiationException e) {
            System.out.println("Instantiation!");
        } catch (NoSuchMethodException e) {
            System.out.println("No such method!");
        } catch (InvocationTargetException e) {
            System.out.println("Invocation target!");
        }
        System.out.println("Done.");
    }

}

如果我运行该示例,我会得到以下输出:

Hello, HAL!
We get a salutation, so Person is now a compiled class in memory loaded by the given ClassLoader.
SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
SLF4J: Defaulting to no-operation (NOP) logger implementation
SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.
Exception in thread "main" java.lang.NoClassDefFoundError: Object (wrong name: Person)
    at java.lang.ClassLoader.defineClass1(Native Method)
    at java.lang.ClassLoader.defineClass(ClassLoader.java:760)
    at java.lang.ClassLoader.defineClass(ClassLoader.java:642)
    at bragaglia.skimmer.core.MemoryFileManager$1.findClass(MemoryFileManager.java:33)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
    at java.lang.Class.forName0(Native Method)
    at java.lang.Class.forName(Class.java:340)
    at org.drools.util.CompositeClassLoader$CachingLoader.load(CompositeClassLoader.java:258)
    at org.drools.util.CompositeClassLoader$CachingLoader.load(CompositeClassLoader.java:237)
    at org.drools.util.CompositeClassLoader.loadClass(CompositeClassLoader.java:88)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
    at org.drools.base.ClassTypeResolver.resolveType(ClassTypeResolver.java:155)
    at org.drools.rule.builder.PatternBuilder.build(PatternBuilder.java:174)
    at org.drools.rule.builder.PatternBuilder.build(PatternBuilder.java:135)
    at org.drools.rule.builder.GroupElementBuilder.build(GroupElementBuilder.java:67)
    at org.drools.rule.builder.RuleBuilder.build(RuleBuilder.java:85)
    at org.drools.compiler.PackageBuilder.addRule(PackageBuilder.java:3230)
    at org.drools.compiler.PackageBuilder.compileRules(PackageBuilder.java:1038)
    at org.drools.compiler.PackageBuilder.compileAllRules(PackageBuilder.java:946)
    at org.drools.compiler.PackageBuilder.addPackage(PackageBuilder.java:938)
    at org.drools.compiler.PackageBuilder.addPackageFromDrl(PackageBuilder.java:470)
    at org.drools.compiler.PackageBuilder.addKnowledgeResource(PackageBuilder.java:698)
    at org.drools.builder.impl.KnowledgeBuilderImpl.add(KnowledgeBuilderImpl.java:51)
    at org.drools.builder.impl.KnowledgeBuilderImpl.add(KnowledgeBuilderImpl.java:40)
    at bragaglia.skimmer.core.Example.main(Example.java:91)

如您所见,Person该类已在内存中成功编译并实例化(请参阅Hello, HAL!输出中的消息),但是如果我将其添加到 WM,Exception in thread "main" java.lang.NoClassDefFoundError: Object (wrong name: Person)即使没有规则明确依赖Persons,我也会得到一个。现在,我稍微调查了一下这个异常,我意识到当在Drools 使用的类Person中找不到给定的类 ( )时,它会被触发。ClassLoader因此,我通过添加一个配置来更改我的代码,该配置引用了ClassLoader用于编译和实例化HALtheKnowledgeBuilder和 的相同配置KnowledgeBase,但是我可能做错了什么,因为我仍然得到相同的异常。

您知道为什么会发生这种情况以及如何解决它吗?提前谢谢了!

4

1 回答 1

0

感谢与我的一个恶魔的讨论(@dsotty,如果你读到这个,我指的是你),我找到了一个解决方案。问题出在我的问题上,MemoryFileManager它只保存了最后一个编译类的字节码。以后每当我试图检索一个类时,我只能找到最后一次编译的结果。Drools 需要访问进入其 WM 的每个类的字节码,因此MemoryFileManager引发了ClassNotFoundException阻塞执行的问题。

现在,解决方案很简单,只需将其private MemoryJavaClassObject object;内部替换为private Map<String, MemoryJavaClassObject> objects;可以成功存储所有类的字节码的位置。因此,每当我尝试编译某些东西时,我也会将字节码存储在其中objects,当我尝试查找一个类时,我首先必须寻找一个带有给定name范围内的条目objects。而已!

为了您的方便和易于理解,代码MemoryFileManager如下:

public class MemoryFileManager extends ForwardingJavaFileManager<StandardJavaFileManager> {

    private Map<String, MemoryJavaClassObject> objects;

    public MemoryFileManager(StandardJavaFileManager manager) {
        super(manager);
        this.objects = new HashMap<>();
    }

    @Override
    public ClassLoader getClassLoader(Location location) {
        return new SecureClassLoader() {
            @Override
            protected Class<?> findClass(String name) throws ClassNotFoundException {
                MemoryJavaClassObject object = objects.get(name);
                if (null == object)
                    throw new ClassNotFoundException("Class '" + name + "' not found.");
                byte[] b = object.getBytes();
                return super.defineClass(name, b, 0, b.length);
            }
        };
    }

    @Override
    public JavaFileObject getJavaFileForOutput(Location location, String name, Kind kind, FileObject sibling) throws IOException {
        MemoryJavaClassObject object = new MemoryJavaClassObject(name, kind);
        objects.put(name, object);
        return object;
    }

}

进一步的改进是可能的,但为简洁起见不包括在内。

于 2014-09-04T10:02:12.613 回答