I'm trying to use a custom ClassLoader
with Drools to add some methods to my facts using javassist
in runtime. Here's the custom ClassLoader
that extends Loader
of javassist
:
@Override
public Class<?> loadClass(String name) throws ClassNotFoundException {
final Class<?> clazz = super.loadClass(name);
if (clazz.getName().equals("my.package.MyClass") {
final ClassPool classPool = ClassPool.getDefault();
final CtClass javassistClass = classPool.get(clazz.getName());
for (final MetaField field : fields) {
final CtMethod method = CtNewMethod.make("public String sampleMethod() {\n" +
" return \"Sample String\"; \n" +
"}");
javassistClass.addMethod(method);
return classPool.toClass(javassistClass, clazz.getClassLoader(), null);
}
return clazz;
}
However, this leaves me with the exception:
java.lang.RuntimeException: Unable to load dialect 'org.drools.compiler.rule.builder.dialect.java.JavaDialectConfiguration:java:org.drools.compiler.rule.builder.dialect.java.JavaDialectConfiguration'
at org.drools.compiler.builder.impl.KnowledgeBuilderConfigurationImpl.addDialect(KnowledgeBuilderConfigurationImpl.java:394)
at org.drools.compiler.builder.impl.KnowledgeBuilderConfigurationImpl.buildDialectConfigurationMap(KnowledgeBuilderConfigurationImpl.java:380)
at org.drools.compiler.builder.impl.KnowledgeBuilderConfigurationImpl.init(KnowledgeBuilderConfigurationImpl.java:235)
at org.drools.compiler.builder.impl.KnowledgeBuilderConfigurationImpl.init(KnowledgeBuilderConfigurationImpl.java:187)
at org.drools.compiler.builder.impl.KnowledgeBuilderConfigurationImpl.<init>(KnowledgeBuilderConfigurationImpl.java:155)
at org.drools.compiler.kie.builder.impl.AbstractKieProject.getBuilderConfiguration(AbstractKieProject.java:302)
at org.drools.compiler.kie.builder.impl.AbstractKieProject.createKnowledgeBuilder(AbstractKieProject.java:288)
at org.drools.compiler.kie.builder.impl.AbstractKieProject.buildKnowledgePackages(AbstractKieProject.java:213)
at org.drools.compiler.kie.builder.impl.AbstractKieProject.verify(AbstractKieProject.java:75)
at org.drools.compiler.kie.builder.impl.KieBuilderImpl.buildKieProject(KieBuilderImpl.java:271)
at org.drools.compiler.kie.builder.impl.KieBuilderImpl.buildAll(KieBuilderImpl.java:239)
at org.drools.compiler.kie.builder.impl.KieBuilderImpl.buildAll(KieBuilderImpl.java:188)
at com.company.project.kie.util.KieRulePopulator.populateInternal(KieRulePopulator.java:112)
at com.company.project.kie.util.KieRulePopulator.populate(KieRulePopulator.java:150)
at com.company.project.kie.domain.rule.KieRuleContainer.initInternal(KieRuleContainer.java:121)
at com.company.project.kie.domain.rule.KieRuleContainer.init(KieRuleContainer.java:105)
at java.base/java.util.stream.ReferencePipeline$11$1.accept(ReferencePipeline.java:441)
at java.base/java.util.stream.ReferencePipeline$2$1.accept(ReferencePipeline.java:177)
at java.base/java.util.ArrayList$ArrayListSpliterator.forEachRemaining(ArrayList.java:1655)
at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:484)
at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:474)
at java.base/java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:913)
at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
at java.base/java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:578)
at com.company.project.kie.domain.rule.dao.JdbcCacheableKieRuleContainerDaoImpl.find(JdbcCacheableKieRuleContainerDaoImpl.java:105)
at com.company.project.kie.domain.rule.dao.JdbcCacheableKieRuleContainerDaoImpl$$FastClassBySpringCGLIB$$9f93eb18.invoke(<generated>)
at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218)
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:769)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163)
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:747)
at org.springframework.cache.interceptor.CacheInterceptor.lambda$invoke$0(CacheInterceptor.java:53)
at org.springframework.cache.interceptor.CacheAspectSupport.invokeOperation(CacheAspectSupport.java:365)
at org.springframework.cache.interceptor.CacheAspectSupport.execute(CacheAspectSupport.java:420)
at org.springframework.cache.interceptor.CacheAspectSupport.execute(CacheAspectSupport.java:345)
at org.springframework.cache.interceptor.CacheInterceptor.invoke(CacheInterceptor.java:61)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:747)
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:689)
at com.company.project.kie.domain.rule.dao.JdbcCacheableKieRuleContainerDaoImpl$$EnhancerBySpringCGLIB$$414ae73c.find(<generated>)
at com.company.client.core.configuration.ClientCachePopulatingApplicationListener$1.<init>(ClientCachePopulatingApplicationListener.java:102)
at com.company.client.core.configuration.ClientCachePopulatingApplicationListener.onApplicationEvent(ClientCachePopulatingApplicationListener.java:101)
at com.company.client.core.configuration.ClientCachePopulatingApplicationListener.onApplicationEvent(ClientCachePopulatingApplicationListener.java:42)
at com.company.client.core.configuration.ClientCachePopulatingApplicationListener$$EnhancerBySpringCGLIB$$d87e98b8.onApplicationEvent(<generated>)
at org.springframework.context.event.SimpleApplicationEventMulticaster.doInvokeListener(SimpleApplicationEventMulticaster.java:172)
at org.springframework.context.event.SimpleApplicationEventMulticaster.invokeListener(SimpleApplicationEventMulticaster.java:165)
at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:139)
at org.springframework.context.support.AbstractApplicationContext.publishEvent(AbstractApplicationContext.java:403)
at org.springframework.context.support.AbstractApplicationContext.publishEvent(AbstractApplicationContext.java:360)
at org.springframework.context.support.AbstractApplicationContext.finishRefresh(AbstractApplicationContext.java:897)
at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.finishRefresh(ServletWebServerApplicationContext.java:162)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:553)
at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:141)
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:747)
at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:397)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:315)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1226)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1215)
at com.company.client.core.NeroFraudGateApplication.main(NeroFraudGateApplication.java:28)
Caused by: java.lang.ClassCastException: class org.drools.compiler.rule.builder.dialect.java.JavaDialectConfiguration cannot be cast to class org.drools.compiler.compiler.DialectConfiguration (org.drools.compiler.rule.builder.dialect.java.JavaDialectConfiguration is in unnamed module of loader com.company.project.kie.util.MetaFieldAwareClassLoader @6dc98c02; org.drools.compiler.compiler.DialectConfiguration is in unnamed module of loader 'app')
at org.drools.compiler.builder.impl.KnowledgeBuilderConfigurationImpl.addDialect(KnowledgeBuilderConfigurationImpl.java:389)
... 57 common frames omitted
Here's and excerpt from javassist
tutorial:
If the program is running on some application server such as JBoss and
Tomcat, the context class loader used bytoClass()
might be
inappropriate. In this case, you would see an unexpectedClassCastException
. To avoid this exception, you must explicitly
give an appropriate class loader totoClass()
. For example, if bean
is your session bean object, then the following code:
CtClass cc = ...; Class c = cc.toClass(bean.getClass().getClassLoader());
would work. You should give
toClass()
the class loader that has
loaded your program (in the above example, the class of the bean
object).
toClass()
is provided for convenience. If you need more complex
functionality, you should write your own class loader.
What am I missing? Thanks.