0
votes

I have an existing project with web services that I'm working to attach Drools to to process business rules on submissions to these services.

Everything seems to work fine in compile-time unit testing, but when I run the project in the Tomcat 7 container on my testing server, I'm getting the following stack trace:

05/25/2017 13:38:53.052 (EDT):[com.myproject.helpers.BusinessRuleHelper.checkBusinessRules]:[SEVERE]- Failed to process our request
05/25/2017 13:38:53.052 (EDT):[com.myproject.helpers.BusinessRuleHelper.checkBusinessRules]:[SEVERE]- Could not initialize class org.drools.compiler.kie.builder.impl.KieContainerImpl
05/25/2017 13:38:53.052 (EDT):[com.myproject.helpers.BusinessRuleHelper.checkBusinessRules]:[SEVERE]- class java.lang.NoClassDefFoundError
05/25/2017 13:38:53.053 (EDT):[com.myproject.helpers.BusinessRuleHelper.checkBusinessRules]:[SEVERE]- java.lang.NoClassDefFoundError: Could not initialize class org.drools.compiler.kie.builder.impl.KieContainerImpl
    at org.drools.compiler.kie.builder.impl.KieServicesImpl.newKieClasspathContainer(KieServicesImpl.java:135)
    at org.drools.compiler.kie.builder.impl.KieServicesImpl.getKieClasspathContainer(KieServicesImpl.java:101)
    at org.drools.compiler.kie.builder.impl.KieServicesImpl.getKieClasspathContainer(KieServicesImpl.java:79)
    at com.myproject.rules.Engine.processRequest(Engine.java:19)
    at com.myproject.helpers.BusinessRuleHelper.checkBusinessRules(BusinessRuleHelper.java:366)
    at com.myproject.helpers.BusinessRuleHelper.checkCriticalErrorBusinessRules(BusinessRuleHelper.java:291)
    at com.myproject.helpers.WebServiceHelper.processWebServiceRequest(WebServiceHelper.java:184)
    at com.myproject.webservicepackage.WebServiceImpl.webService(WebServiceImpl.java:40)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
    at java.lang.reflect.Method.invoke(Unknown Source)
    at org.apache.cxf.service.invoker.AbstractInvoker.performInvocation(AbstractInvoker.java:172)
    at org.apache.cxf.service.invoker.AbstractInvoker.invoke(AbstractInvoker.java:88)
    at org.apache.cxf.jaxws.JAXWSMethodInvoker.invoke(JAXWSMethodInvoker.java:63)
    at org.apache.cxf.service.invoker.AbstractInvoker.invoke(AbstractInvoker.java:74)
    at org.apache.cxf.interceptor.ServiceInvokerInterceptor$1.run(ServiceInvokerInterceptor.java:58)
    at java.util.concurrent.Executors$RunnableAdapter.call(Unknown Source)
    at java.util.concurrent.FutureTask.run(Unknown Source)
    at org.apache.cxf.workqueue.SynchronousExecutor.execute(SynchronousExecutor.java:37)
    at org.apache.cxf.interceptor.ServiceInvokerInterceptor.handleMessage(ServiceInvokerInterceptor.java:106)
    at org.apache.cxf.phase.PhaseInterceptorChain.doIntercept(PhaseInterceptorChain.java:262)
    at org.apache.cxf.transport.ChainInitiationObserver.onMessage(ChainInitiationObserver.java:123)
    at org.apache.cxf.transport.http.AbstractHTTPDestination.invoke(AbstractHTTPDestination.java:206)
    at org.apache.cxf.transport.servlet.ServletController.invokeDestination(ServletController.java:213)
    at org.apache.cxf.transport.servlet.ServletController.invoke(ServletController.java:193)
    at org.apache.cxf.transport.servlet.CXFNonSpringServlet.invoke(CXFNonSpringServlet.java:127)
    at org.apache.cxf.transport.servlet.AbstractHTTPServlet.handleRequest(AbstractHTTPServlet.java:187)
    at org.apache.cxf.transport.servlet.AbstractHTTPServlet.doPost(AbstractHTTPServlet.java:110)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:646)
    at org.apache.cxf.transport.servlet.AbstractHTTPServlet.service(AbstractHTTPServlet.java:166)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:303)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
    at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:203)
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:122)
    at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:501)
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:171)
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:102)
    at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:950)
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:116)
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:408)
    at org.apache.coyote.ajp.AjpProcessor.process(AjpProcessor.java:193)
    at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:607)
    at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:314)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
    at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
    at java.lang.Thread.run(Unknown Source)

The last line frame under my control, in com.myproject.rules.Engine.processRequest is

/**
 * Runs a Request through the business rule engine
 * @param request The input to validate
 */
public static KieSession processRequest(Request request) {
    // load up the knowledge base
    KieServices ks = KieServices.Factory.get();
    KieContainer kContainer = ks.getKieClasspathContainer(); // <- Error happens here
    // This name matches the ksessionname of the rules knowledge base in the kmodule.xml file
    String kSessionName = "ksession-rules";
    KieSession kSession = kContainer.newKieSession(kSessionName);

    // ...process the rules...
}

I'm confused, because the com.myproject.webservicepackage WAR file contains the Drools JARs in WEB-INF\lib\ the way you would expect, and the last few frames are in the same library with the class that Java can't find the definition for.

The Drools JARs I see in the library folder are:

  • drools-compiler-6.5.0.Final.jar
  • drools-core-6.5.0.Final.jar
  • kie-api-6.5.0.Final.jar
  • kie-internal-6.5.0.Final.jar

As far as I can tell, there's no overlap between these, and the first one is the library in question.


I've continued debugging this, and it looks like this is only the error encountered the second and subsequent requests. The first request came back with this stack trace:

Caused by: java.lang.LinkageError: loader constraint violation: when resolving method "org.slf4j.impl.StaticLoggerBinder.getLoggerFactory()Lorg/slf4j/ILoggerFactory;" the class loader (instance of org/apache/catalina/loader/WebappClassLoader) of the current class, org/slf4j/LoggerFactory, and the class loader (instance of org/apache/catalina/loader/StandardClassLoader) for resolved class, org/slf4j/impl/StaticLoggerBinder, have different Class objects for the type LoggerFactory; used in the signature
    at org.slf4j.LoggerFactory.getILoggerFactory(LoggerFactory.java:299)
    at org.slf4j.LoggerFactory.getLogger(LoggerFactory.java:269)
    at org.slf4j.LoggerFactory.getLogger(LoggerFactory.java:281)
    at org.drools.compiler.kie.builder.impl.KieContainerImpl.<clinit>(KieContainerImpl.java:92)
    at org.drools.compiler.kie.builder.impl.KieServicesImpl.newKieClasspathContainer(KieServicesImpl.java:135)
    at org.drools.compiler.kie.builder.impl.KieServicesImpl.getKieClasspathContainer(KieServicesImpl.java:101)
    at org.drools.compiler.kie.builder.impl.KieServicesImpl.getKieClasspathContainer(KieServicesImpl.java:79)
    at com.myproject.helpers.rules.Engine.processRequest(Engine.java:19)
    at com.myproject.helpers.BusinessRuleHelper.checkBusinessRules(BusinessRuleHelper.java:366)
    at com.myproject.helpers.BusinessRuleHelper.checkCriticalErrorBusinessRules(BusinessRuleHelper.java:291)
    at com.myproject.helpers.WebServiceHelper.processWebServiceRequest(WebServiceHelper.java:184)
    at com.myproject.webservicepackage.WebServiceImpl.webService(WebServiceImpl.java:40)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
    at java.lang.reflect.Method.invoke(Unknown Source)
    at org.apache.cxf.service.invoker.AbstractInvoker.performInvocation(AbstractInvoker.java:172)
    at org.apache.cxf.service.invoker.AbstractInvoker.invoke(AbstractInvoker.java:88)
    ... 37 more

It looks like it's caching this failure as not having a class definition.

1
What's the full name of the jar drools-compiler*.jar? Do you have more than one jar matching the pattern in your classpath? Do they all contain the missing file?laune
@laune, edited the question. There's only the one compiler JAR in the classpath.Kenny Dewhirst
I've looked at the source and it seems that you have a class loader issue. Code in KieServicesImpl calls org.drools.core.common.ProjectClassLoader.findParentClassLoader and I can't tell what this turns up. Does your code do anything that might affect the result of this call?laune
I don't think so, but I've read some FUD about things not working inside of Tomcat because of how it builds the classpath.Kenny Dewhirst

1 Answers

0
votes

This message:

java.lang.LinkageError: loader constraint violation: when resolving method "org.slf4j.impl.StaticLoggerBinder.getLoggerFactory()Lorg/slf4j/ILoggerFactory;" the class loader of the current class, org/slf4j/LoggerFactory, and the class loader for resolved class, org/slf4j/impl/StaticLoggerBinder, have different Class objects for the type LoggerFactory; used in the signature

...indicates a conflict between versions of SLF4J in my project. The library is being provided by some dependency I'm using and also by the Tomcat container itself. I used Maven to find what artifact is using SLF4J by running:

mvn dependency:tree -Dverbose -Dincludes=org.slf4j

This got me a dependency tree that shows where SLF4J is being used:

[INFO] ------------------------------------------------------------------------
[INFO] Building business-rules 3.4-SNAPSHOT
[INFO] ------------------------------------------------------------------------
[INFO]
[INFO] --- maven-dependency-plugin:2.8:tree (default-cli) @ business-rules ---
[INFO] com.myproject:business-rules:jar:3.4-SNAPSHOT
[INFO] +- org.kie:kie-api:jar:6.5.0.Final:compile
[INFO] |  \- org.slf4j:slf4j-api:jar:1.7.2:compile
[INFO] +- org.drools:drools-core:jar:6.5.0.Final:compile
[INFO] |  +- org.kie:kie-internal:jar:6.5.0.Final:compile
[INFO] |  |  \- (org.slf4j:slf4j-api:jar:1.7.2:compile - omitted for duplicate)
[INFO] |  \- (org.slf4j:slf4j-api:jar:1.7.2:compile - omitted for duplicate)
[INFO] \- org.drools:drools-compiler:jar:6.5.0.Final:compile
[INFO]    \- (org.slf4j:slf4j-api:jar:1.7.2:compile - omitted for duplicate)

I was able to remove the version brought in here by modifying the Maven POM for the project to exclude SLF4J in each of those three dependencies:

<dependencies>
  ...
  <dependency>
      <groupId>org.kie</groupId>
      <artifactId>kie-api</artifactId>
      <version>${drools.version}</version>
      <exclusions> 
        <exclusion> 
          <groupId>org.slf4j</groupId> 
          <artifactId>slf4j-api</artifactId> 
        </exclusion> 
    </exclusions> 
  </dependency>
  <dependency>
      <groupId>org.drools</groupId>
      <artifactId>drools-core</artifactId>
      <version>${drools.version}</version>
      <exclusions> 
        <exclusion> 
          <groupId>org.slf4j</groupId> 
          <artifactId>slf4j-api</artifactId> 
        </exclusion> 
    </exclusions> 
  </dependency>
  <dependency>
      <groupId>org.drools</groupId>
      <artifactId>drools-compiler</artifactId>
      <version>${drools.version}</version>
      <exclusions> 
        <exclusion> 
          <groupId>org.slf4j</groupId> 
          <artifactId>slf4j-api</artifactId> 
        </exclusion> 
    </exclusions> 
  </dependency>
  ...
</dependencies>

And then in order to get it to build and test locally, I still had to add an explicit compile-time dependency on SLF4J in that same project, and also the one that imported it, to let Maven know that the Tomcat container will provide the dependency at runtime:

<dependencies>
  ...
  <dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-api</artifactId>
    <version>1.7.2</version>
    <scope>provided</scope>
  </dependency>
  ...
</dependencies>

These changes seem to have fixed my problems.