0

XML ファイルを読み取り、それらのノードを POJO に変換し、それらを Drools の WM にロードし、最終的にそれらにいくつかのルールを適用する単純なユーティリティを作成しています。プロジェクト全体は、私のGitHub プロファイルで見つけることができます。残念ながら、あらゆる努力にもかかわらず、実行時にコンパイルされたクラスのインスタンスを Drools に「いいね」させることはできませんでした。多くの人が ClassLoader にも問題を抱えているのを見たので、それが原因であると思われます... GitHubと以下で入手できる最小限の作業例を用意しました。GitHubでのみ利用可能な他のいくつかの小さなファイル ( 、および) が必要です。MemoryFileManagerMemoryJavaClassObjectMemoryJavaFileObject簡潔にするために。この例を正しく機能させるには、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)明示的に s に依存するルールがない場合でもエラーが発生しPersonます。ここで、例外を少し調べたところ、指定されたクラス ( ) がDrools によって使用されるクラスPerson内に見つからない場合に発生することがわかりました。したがって、と の両方にコンパイルおよびインスタンス化するために使用されるものとClassLoaderまったく同じ参照を含む構成を追加して、コードを変更しましたが、それでも同じ例外が発生するため、何か間違っている可能性があります。ClassLoaderHALKnowledgeBuilderKnowledgeBase

なぜこれが起こるのか、それを回避する方法はありますか?よろしくお願いします!

4

1 に答える 1

0

私の悪魔 (@dsotty、これを読んでいるなら、私はあなたのことを指しています) との議論のおかげで、私は解決策を見つけました。問題はMemoryFileManager、最後にコンパイルされたクラスのバイトコードのみを保存していた私のものでした。後でクラスを取得しようとしたときはいつでも、最後のコンパイルの結果しか見つけることができませんでした。MemoryFileManagerDroolsは、その WM に入る各クラスのバイト コードにアクセスする必要があるため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 に答える