Почему загрузка классов Java терпит неудачу в Linux, но успешна в Windows? - PullRequest
2 голосов
/ 06 февраля 2009

У меня есть веб-приложение на Java (с использованием Spring), развернутое с помощью Jetty. Если я попытаюсь запустить его на компьютере с Windows, все будет работать как положено, но если я попытаюсь запустить тот же код на моей машине с Linux, произойдет сбой следующим образом:

[normal startup output]
11:16:39.657 INFO   [main] org.mortbay.jetty.servlet.ServletHandler$Context.log>(ServletHandler.java:1145) >16> Set web app root system property: 'webapp.root' = [/path/to/working/dir]
java.lang.reflect.InvocationTargetException
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
        at java.lang.reflect.Method.invoke(Method.java:597)
        at org.mortbay.start.Main.invokeMain(Main.java:151)
        at org.mortbay.start.Main.start(Main.java:476)
        at org.mortbay.start.Main.main(Main.java:94)
Caused by: java.lang.ExceptionInInitializerError
        at org.springframework.web.util.Log4jWebConfigurer.initLogging(Log4jWebConfigurer.java:129)
        at org.springframework.web.util.Log4jConfigListener.contextInitialized(Log4jConfigListener.java:51)
        at org.mortbay.jetty.servlet.WebApplicationContext.doStart(WebApplicationContext.java:495)
        at org.mortbay.util.Container.start(Container.java:72)
        at org.mortbay.http.HttpServer.doStart(HttpServer.java:708)
        at org.mortbay.util.Container.start(Container.java:72)
        at org.mortbay.jetty.Server.main(Server.java:460)
        ... 7 more
Caused by: org.apache.commons.logging.LogConfigurationException: org.apache.commons.logging.LogConfigurationException: No suitable Log constructor [Ljava.lang.Class;@15311bd for org.apache.commons.logging.impl.Log4JLogger (Caused by java.lang.NoClassDefFoundError: org/apache/log4j/Category) (Caused by org.apache.commons.logging.LogConfigurationException: No suitable Log constructor [Ljava.lang.Class;@15311bd for org.apache.commons.logging.impl.Log4JLogger (Caused by java.lang.NoClassDefFoundError: org/apache/log4j/Category))
        at org.apache.commons.logging.impl.LogFactoryImpl.newInstance(LogFactoryImpl.java:543)
        at org.apache.commons.logging.impl.LogFactoryImpl.getInstance(LogFactoryImpl.java:235)
        at org.apache.commons.logging.impl.LogFactoryImpl.getInstance(LogFactoryImpl.java:209)
        at org.apache.commons.logging.LogFactory.getLog(LogFactory.java:351)
        at org.springframework.util.SystemPropertyUtils.(SystemPropertyUtils.java:42)
        ... 14 more
Caused by: org.apache.commons.logging.LogConfigurationException: No suitable Log constructor [Ljava.lang.Class;@15311bd for org.apache.commons.logging.impl.Log4JLogger (Caused by java.lang.NoClassDefFoundError: org/apache/log4j/Category)
        at org.apache.commons.logging.impl.LogFactoryImpl.getLogConstructor(LogFactoryImpl.java:413)
        at org.apache.commons.logging.impl.LogFactoryImpl.newInstance(LogFactoryImpl.java:529)
        ... 18 more
Caused by: java.lang.NoClassDefFoundError: org/apache/log4j/Category
        at java.lang.Class.getDeclaredConstructors0(Native Method)
        at java.lang.Class.privateGetDeclaredConstructors(Class.java:2389)
        at java.lang.Class.getConstructor0(Class.java:2699)
        at java.lang.Class.getConstructor(Class.java:1657)
        at org.apache.commons.logging.impl.LogFactoryImpl.getLogConstructor(LogFactoryImpl.java:410)
        ... 19 more
Caused by: java.lang.ClassNotFoundException: org.apache.log4j.Category
        at java.net.URLClassLoader$1.run(URLClassLoader.java:200)
        at java.security.AccessController.doPrivileged(Native Method)
        at java.net.URLClassLoader.findClass(URLClassLoader.java:188)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:307)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:252)
        at java.lang.ClassLoader.loadClassInternal(ClassLoader.java:320)
        ... 24 more
[shutdown output]

Я запустил приложение с java -verbose:class, и в соответствии с этим выводом org.apache.log4j.Category загружается из JAR log4j в моем / WEB-INF / lib незадолго до того, как выдается первое исключение.

Теперь версии Java на двух машинах немного отличаются. Обе машины имеют java от Sun, на машине Linux - 1.6.0_10, а на машине Windows - 1.6.0_08, или, может быть, 07 или 06, я не могу вспомнить точное число прямо сейчас, и у меня нет машины под рукой , Но даже если второстепенные версии Javas немного отличаются, код не должен так ломаться. Кто-нибудь понимает, что здесь не так?

Ответы [ 6 ]

4 голосов
/ 06 февраля 2009

Вы должны понимать, что загрузчик классов не может видеть все; они могут видеть только то, что загрузил родительский загрузчик классов или что они загрузили сами. Поэтому, если у вас есть два загрузчика классов, скажем, один для Jetty, а другой для вашего веб-приложения, ваше веб-приложение может видеть log4j (поскольку JAR - это WEB-INF / lib), а загрузчик классов Jetty - нет.

Если вам удастся сделать класс доступным для Jetty (например, что-то на уровне БД), который использует log4j, но в конечном итоге работает в контексте (и загрузчике классов) Jetty, вы получите ошибку.

Чтобы отладить это, установите точку останова в org.springframework.web.util.Log4jWebConfigurer.initLogging (). Если вы можете, скопируйте исходный код этого класса в свой проект (не забудьте впоследствии удалить его) и добавьте следующую строку:

ClassLoader cl = Thread.currentThread().getContextClassLoader();

Посмотрите на объект cl в вашем отладчике. Это должно дать вам некоторую информацию, кто его создал. Я предполагаю, что это загрузчик классов от Jetty.

[EDIT] Обратите внимание, что вы попадаете в другой беспорядок, если у вас есть log4j в обоих загрузчиках классов: в этом случае у вас будет два класса с одинаковым именем, которые создают объекты которые не совместимы с назначением! Поэтому убедитесь, что существует только один экземпляр этого jar, или что экземпляры log4j никогда не будут передаваться между двумя контекстами (что обычно невозможно).

1 голос
/ 06 февраля 2009

Это похоже на классическую проблему загрузчика классов. Это может быть связано с тем, что сначала загружается другое веб-приложение, которое также использует log4j, но версия отличается от версии, используемой тестируемым приложением. Загрузчик классов использует первую версию найденного класса. Политику загрузки классов серверов обычно можно изменить в файлах конфигурации. Извините, я немного устала от этого, но, возможно, это может указать вам правильное направление.

  1. Убедитесь, что на веб-сервере нет других установленных приложений,
  2. Убедитесь, что загружаемый log4j - правильная версия,
  3. Убедитесь, что у вас нет log4j, скрывающегося где-то в пути к классам сервера.

НТН

0 голосов
/ 28 ноября 2011

Была такая же проблема и нашли простое решение / обходной путь:

В Eclipse в «Предпочтения»> «Java»> «Установленные JRE» выберите «JRE»> «Редактировать и добавить внешние JAR» ... и найдите файл log4j.jar.

Другим обходным решением является добавление log4j.jar к каждому определению запуска на вкладке Classpath.

0 голосов
/ 24 апреля 2009

Несмотря на то, что исходная проблема была решена для автора вопроса, я укажу, что распространенным источником проблем при запуске одного и того же кода в Windows против Linux (или Unix) являются проблемы с учетом регистра. Windows игнорирует регистр, тогда как Linux или Unix чувствительны к регистру. Это укусило меня не раз.

Так что, если вы указываете jar или каталог на пути к классам, но это не тот случай, тогда это приведет к сбою в Linux, но будет успешным в Windows. Это также может быть источником FileNotFoundExceptions.

0 голосов
/ 06 февраля 2009

Несколько случайных вещей для рассмотрения:

(1) Проверить, есть ли какие-либо другие версии log4j, плавающие в экземпляре linux вне каталогов веб-приложения?

(2) Используется ли вообще регистрация в Apache commons? Возможно, вы захотите рассмотреть SLF4J вместо?

(3) JAR / WAR каким-то образом испортился - был ли это протокол FTP в ASCII или в двоичном формате?

(4) Распечатывать иерархию загрузчиков классов в каждом случае, просто чтобы увидеть, есть ли какие-либо расхождения?

0 голосов
/ 06 февраля 2009

Вы используете одну и ту же WAR на обеих машинах? Вы проверили, идентичны ли файлы WAR (не было ошибок передачи)?

...