1

I am trying to Load the class using my custom class loader and create rule for the loaded class

public class DynamicClassLoader extends DynamicProjectClassLoader {

    private static DynamicClassLoader instance;

    public static DynamicClassLoader getInstance() {
        if(instance == null) {
            instance = new DynamicClassLoader();
        }
        return instance;
    }

    public DynamicClassLoader() {
        super(ClassLoader.getSystemClassLoader(), (ResourceProvider) null);
    }

    public Class<?> classFromFile(String filepath) {
        try {
            BufferedInputStream input = new BufferedInputStream(new FileInputStream(filepath));
            
            ByteArrayOutputStream buffer = new ByteArrayOutputStream();
            int data = input.read();
            while(data != -1){
                buffer.write(data);
                data = input.read();
            }
            byte[] classData = buffer.toByteArray();
            
            input.close();

            return defineClass(null, classData, 0, classData.length);
        } catch (IOException | ClassFormatError e) {
            e.printStackTrace();
        }

        return null;
    }
}

In this process I am getting the below Rule Compilation Errors:

  • Only a type can be imported. com.sams.Test resolves to a package
  • com.sams.Test cannot be resolved to a type

I am trying to create a stateless session

private StatelessKieSession createKieBase(String rule) {
        KnowledgeBuilderConfiguration kbconf = KnowledgeBuilderFactory.
            newKnowledgeBuilderConfiguration(null, classLoader);

        KieBaseConfiguration kieBaseConf = kieServices.newKieBaseConfiguration();
        kieBaseConf.setOption(SequentialOption.YES);
        kieBaseConf.setOption(MultithreadEvaluationOption.YES);
        KnowledgeBuilder kbuilder = KnowledgeBuilderFactory.newKnowledgeBuilder(kbconf);

        kbuilder.add(ResourceFactory.newByteArrayResource(rule.getBytes()), ResourceType.DRL);

        if (kbuilder.hasErrors()) {
            kbuilder.undo();
        }
        InternalKnowledgeBase kbase = KnowledgeBaseFactory.newKnowledgeBase(kieBaseConf);
        kbase.addPackages(kbuilder.getKnowledgePackages());
        
        return kbase.newStatelessKieSession();
    }

Error stack trace:

Rule Compilation error : [Rule name='Rule1'] org.drools.compiler.kie.builder.impl.CompilationProblemAdapter@3aa8ae86 org.drools.compiler.kie.builder.impl.CompilationProblemAdapter@40bd52a4

  • com/test/Rule_Rule1753680621.java (2:43) : Only a type can be imported. com.test.Test resolves to a package
  • com/test/Rule_Rule1753680621.java (6:255) : com.test.Test cannot be resolved to a type

Sample Rule

package com.test;
import com.test.Test;

rule "Rule1"
when
$subject : Test()

Test(test == "582955")

then
$subject.setTestObjective("abc");
end

Test Class

package com.test;

public class Test {
    public Test() {
    }

    private String tesAbc;
    private String testObjective;

    public String getTesAbc() {
        return tesAbc;
    }

    public void setTesAbc(String tesAbc) {
        this.tesAbc = tesAbc;
    }

    public String getTestObjective() {
        return testObjective;
    }

    public void setTestObjective(String testObjective) {
        this.testObjective = testObjective;
    }
}

UPDATE I tried creating session with Drools 8.37.0.Final but still it's not working

private void testKieSession(String rule) throws ClassNotFoundException {
        KieServices ks = KieServices.Factory.get();
        KieFileSystem kfs = ks.newKieFileSystem();
        kfs.write( "src/main/resources/myDrl.txt",rule);
        ReleaseId releaseId = ks.newReleaseId("com.sample", "my-sample-a", "1.0.0");
        kfs.generateAndWritePomXML(releaseId);
        KieBuilder kieBuilder = ks.newKieBuilder(kfs).buildAll();
        KieContainer kcontainer = ks.newKieContainer(releaseId,classLoader);
        Class<?> classA = kcontainer.getClassLoader().loadClass("com.test.Test"); // I am able to load class from kcontainer
        KieBase kieBase = kcontainer.getKieBase(); // This Kiebase does not contain my drl and Test.class
        KieSession ksession = kieBase.newKieSession(); // This kiesession does not contain my drl and Test.class
        ReteDumper.dumpRete(kcontainer.getKieBase());
    }

1 Answers1

0

"com/test/Rule_Rule1753680621.java (2:43) : Only a type can be imported. com.test.Test resolves to a package" leads me to believe getResourceAsStream(String) is not correctly implemented in your custom ClassLoader. It should return an input stream containing the bytecode for the generated class. I would modify DynamicClassLoader like so:

public class DynamicClassLoader extends DynamicProjectClassLoader {

    private static DynamicClassLoader instance;
    private Map<String, byte[]> classNameToBytecode = new HashMap<>();

    public static DynamicClassLoader getInstance() {
        if(instance == null) {
            instance = new DynamicClassLoader();
        }
        return instance;
    }

    public DynamicClassLoader() {
        super(ClassLoader.getSystemClassLoader(), (ResourceProvider) null);
    }

    public Class<?> classFromFile(String filepath) {
        try {
            BufferedInputStream input = new BufferedInputStream(new FileInputStream(filepath));
            
            ByteArrayOutputStream buffer = new ByteArrayOutputStream();
            int data = input.read();
            while(data != -1){
                buffer.write(data);
                data = input.read();
            }
            byte[] classData = buffer.toByteArray();
            
            input.close();
            // TODO: get class name from classData
            // See https://stackoverflow.com/a/1650442 for an example
            // classNameToBytecode.put(className, classData);

            return defineClass(null, classData, 0, classData.length);
        } catch (IOException | ClassFormatError e) {
            e.printStackTrace();
        }

        return null;
    }

    @Override
    public InputStream getResourceAsStream(String name) {
        if (name.endsWith(".class")) {
            String resourceClassName = name.substring(0, name.length() - 6)
                        .replace('/', '.');
            if (classNameToBytecode.containsKey(resourceClassName)) {
                return new ByteArrayInputStream(classNameToBytecode.get(resourceClassName));
            }
        }
        return super.getResourceAsStream(name);
    }
}
Christopher Chianelli
  • 1,163
  • 1
  • 8
  • 8