2014-09-03 2 views
0

XML 파일을 읽고 노드를 POJO로 변환 한 다음 Drools의 WM로로드하고 마지막으로 적용하는 간단한 유틸리티를 작성합니다. 그들에게 어떤 규칙들. 내 GitHub profile에서 전체 프로젝트를 찾을 수 있습니다. 불행히도, 모든 노력에도 불구하고 Drools는 런타임에 컴파일 된 클래스의 인스턴스를 "좋아"하게 만들 수 없었습니다. 나는 많은 사람들이 ClassLoader에 문제가있는 것을 보았습니다. 그래서 그것의 잘못이라고 생각합니다 ... 최소 작업 예제은 시도 할 수 있습니다.이 파일은 GitHub과 아래에 있습니다. 다른 작은 파일 (MemoryFileManager, MemoryJavaClassObjectMemoryJavaFileObject)이 필요하며 간결성을 위해 GitHub에서만 사용할 수 있습니다. 제대로 작동하려면 JVM이 JDK> = 6이고 classpathtools.jar 또는 classes.jar이 있어야합니다.Drools 5.6.0으로 ClassLoader를 공유하여 런타임에 클래스의 메모리 내 컴파일 가능

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에 추가하면 어떤 규칙도 명시 적으로 Person에 의존하지 않아도 Exception in thread "main" java.lang.NoClassDefFoundError: Object (wrong name: Person)이됩니다. 이제 예외를 조금 조사한 결과, 주어진 클래스 (Person)가 Drools에 의해 사용 된 ClassLoader 안에 없으면 해고된다는 것을 깨달았습니다. 따라서 ClassLoaderHALKnowledgeBuilderKnowledgeBase 모두에 컴파일하고 인스턴스화하는 데 사용 된 참조와 함께 구성을 추가했지만 동일한 예외가 발생했기 때문에 문제가 발생할 수 있습니다.

왜 이런 일이 발생하고 어떻게 해결할 수 있는지 알고 있습니까? 미리 많은 감사드립니다!

답변

0

광인들과의 토론 덕분에 (@sotty, 이것을 읽으면 나는 당신을 언급하고있다), 나는 해결책을 발견했다. 마지막으로 컴파일 된 클래스의 바이트 코드 만 저장하는 내 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; 
    } 

} 

또한 개선이 가능하지만 간결 포함되지 않습니다.