2014-09-03 10 views
0

Я пишу простую утилиту, которая читает XML-файлы, преобразует их узлы в POJO, загружает их в WM Drools и, наконец, применяет некоторые правила для них. Вы можете найти весь проект на моем GitHub profile. К сожалению, несмотря на все мои усилия, я не мог заставить Drools «понравиться» любому экземпляру классов, которые были скомпилированы во время выполнения. Я видел, что у многих людей также были проблемы с ClassLoader, поэтому я подозреваю, что это может быть его ошибка ... Я подготовил Минимальный рабочий пример, который вы можете попробовать, который доступен на GitHub и ниже. Для этого требуется несколько других небольших файлов (MemoryFileManager, MemoryJavaClassObject и MemoryJavaFileObject), которые доступны только на GitHub для краткости. Для правильной работы этот пример требует, чтобы ваша JVM была JDK> = 6, и что у вас есть tools.jar или classes.jar на вашем classpath. Примером может служить следующее:Совместное использование ClassLoader с Drools 5.6.0, чтобы разрешить компиляцию классов в памяти во время выполнения

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), даже если никакое правило явно не использует Person s. Теперь я немного изучил это исключение, и я понял, что его увольняют, когда данный класс (Person) не найден в пределах ClassLoader, используемого Drools. Поэтому я изменил свой код, добавив конфигурацию со ссылкой на тот же самый ClassLoader, который использовался для компиляции и создания экземпляра HAL как для KnowledgeBuilder, так и для KnowledgeBase, однако я мог бы сделать что-то неправильно, потому что я все еще получаю то же исключение.

У вас есть идеи, почему это происходит и как это работает? Спасибо заранее!

ответ

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; 
    } 

} 

Дальнейшие усовершенствования возможны, но не включены для краткости.

 Смежные вопросы

  • Нет связанных вопросов^_^