Что могло вызвать ошибку NoClassDefFoundError?

У меня есть существующий проект с веб-службами, к которому я работаю, чтобы присоединить Drools для обработки бизнес-правил при отправке в эти службы.

Кажется, что все работает нормально при модульном тестировании во время компиляции, но когда я запускаю проект в контейнере Tomcat 7 на моем тестовом сервере, я получаю следующую трассировку стека:

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)

Последний строчный фрейм под моим контролем в com.myproject.rules.Engine.processRequest

/**
 * 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...
}

Я сбит с толку, потому что файл com.myproject.webservicepackage WAR содержит файлы Drools JAR в WEB-INF\lib\, как и следовало ожидать, а последние несколько фреймов находятся в той же библиотеке, что и класс, определение для которого Java не может найти.

В папке библиотеки я вижу следующие JAR-файлы Drools:

  • слюни компилятор-6.5.0.Final.jar
  • слюни-core-6.5.0.Final.jar
  • kie-api-6.5.0.Final.jar
  • ки-внутренний-6.5.0.Final.jar

Насколько я могу судить, между ними нет совпадения, и первая из них - это рассматриваемая библиотека.


Я продолжил отладку, и похоже, что это только ошибка, обнаруженная во втором и последующих запросах. Первый запрос вернулся с такой трассировкой стека:

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

Похоже, он кэширует этот сбой как не имеющий определения класса.


person Kenny Dewhirst    schedule 25.05.2017    source источник
comment
Какое полное имя jar-слюнявого компилятора * .jar? У вас есть несколько банок, соответствующих шаблону в вашем пути к классам? Все ли они содержат недостающий файл?   -  person laune    schedule 25.05.2017
comment
@laune, отредактировал вопрос. В пути к классам есть только один JAR компилятора.   -  person Kenny Dewhirst    schedule 25.05.2017
comment
Я просмотрел исходный код и, похоже, у вас проблема с загрузчиком классов. Код в KieServicesImpl вызывает org.drools.core.common.ProjectClassLoader.findParentClassLoader, и я не могу сказать, что это происходит. Ваш код делает что-нибудь, что может повлиять на результат этого вызова?   -  person laune    schedule 25.05.2017
comment
Я так не думаю, но я читал FUD о вещах, которые не работают внутри Tomcat из-за того, как он строит путь к классам.   -  person Kenny Dewhirst    schedule 25.05.2017


Ответы (1)


Это сообщение:

java.lang.LinkageError: нарушение ограничения загрузчика: при разрешении метода «org.slf4j.impl.StaticLoggerBinder.getLoggerFactory()Lorg/slf4j/ILoggerFactory;» загрузчик классов текущего класса org/slf4j/LoggerFactory и загрузчик классов для разрешенного класса org/slf4j/impl/StaticLoggerBinder имеют разные объекты класса для типа LoggerFactory; используется в подписи

... указывает на конфликт между версиями SLF4J в моем проекте. Библиотека предоставляется какой-то зависимостью, которую я использую, а также самим контейнером Tomcat. Я использовал Maven, чтобы найти, какой артефакт использует SLF4J, запустив:

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

Это дало мне дерево зависимостей, которое показывает, где используется SLF4J:

[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)

Мне удалось удалить версию, представленную здесь, изменив Maven POM для проекта, чтобы исключить SLF4J в каждой из этих трех зависимостей:

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

А затем, чтобы заставить его собирать и тестировать локально, мне все равно пришлось добавить явную зависимость времени компиляции от SLF4J в том же проекте, а также тот, который его импортировал, чтобы Maven знал, что контейнер Tomcat предоставит зависимость во время выполнения:

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

Эти изменения, кажется, устранили мои проблемы.

person Kenny Dewhirst    schedule 26.05.2017