NoClassDefFoundError в JFace FontRegistry - PullRequest
       12

NoClassDefFoundError в JFace FontRegistry

3 голосов
/ 28 февраля 2009

Когда я запускаю приложение SWT (через профиль запуска Eclipse), я получаю следующую трассировку стека:

Exception in thread "main" java.lang.NoClassDefFoundError: org/eclipse/jface/resource/FontRegistry
    at org.eclipse.jface.resource.JFaceResources.getFontRegistry(JFaceResources.java:338)
    at org.eclipse.jface.window.Window.close(Window.java:313)
    at org.eclipse.jface.dialogs.Dialog.close(Dialog.java:971)
    at org.eclipse.jface.dialogs.ProgressMonitorDialog.close(ProgressMonitorDialog.java:348)
    at org.eclipse.jface.dialogs.ProgressMonitorDialog.finishedRun(ProgressMonitorDialog.java:582)
    at org.eclipse.jface.dialogs.ProgressMonitorDialog.run(ProgressMonitorDialog.java:498)
    at com.blah.si.workflow.SWTApplication.main(SWTApplication.java:135)

Теперь, вещи, которые делают это странным:

  1. Когда я изменяю путь сборки проекта и заменяю jface.jar исходным проектом (та же версия - 3.3.1), ошибка исчезает.
  2. Другие приложения, которые у меня есть, используют тот же jar и копию того же профиля запуска и проекта, все работает нормально.
  3. Это НЕ a ClassNotFoundException. Класс находится на пути к классам. Если я присоединяю источник к банке, я могу отладить метод getFontRegistry. Метод будет успешно выполнен несколько раз, прежде чем в конечном итоге вывести NoClassDefFoundError в строку 338. Строка 337 - это оператор «if variable == null», проверяющий, была ли инициализирована статическая переменная. Строка 338 инициализирует ее, если она еще не инициализирована. В первый раз проверка на ноль завершается неудачно, и выполняется инициализация. При последующих прохождениях через метод проходит нулевая проверка и, таким образом, возвращается уже инициализированное статическое значение. На последнем проходе (тот, который терпит неудачу), проверка нуля снова терпит неудачу (даже если статическая переменная уже была инициализирована), и когда она пытается повторно инициализировать статическую переменную, NoClassDefFoundError бросается. Вот соответствующий источник (начиная со строки 336, обратите внимание, что fontRegistry является закрытой статической переменной, которая не установлена ​​ни в каком другом месте):

.

public static FontRegistry getFontRegistry() {
   if (fontRegistry == null) {
     fontRegistry = new FontRegistry(
         "org.eclipse.jface.resource.jfacefonts");
   }
   return fontRegistry;
}

.

  1. Я уже получил свежую копию файла jar (чтобы убедиться, что он не поврежден), удалил мои файлы .classpath и .project, запустил новый проект и заново создал профиль запуска. Без изменений.

Из-за особенностей в # 3 выше, я подозреваю какое-то странное поведение загрузчика классов - кажется, что последний проход через метод находится в другом загрузчике классов?

Идеи

Обновление: Ответ, предоставленный Pourquoi Litytestdata, побудил меня обратить внимание на то, что происходит в блоке try чуть выше строки 458 ProgressMonitorDialog. Действительно, этот код вызывал исключение, которое поглощалось блоком finally. Основной причиной было ДРУГОЕ отсутствие класса (отсутствующим классом был не JFontRegistry или какой-либо из его напрямую связанных классов, а другой, который зависел от паутины в крайнем случае.) и принимая Pourquoi, потому что это был прорыв. Спасибо всем.

Ответы [ 5 ]

3 голосов
/ 28 февраля 2009

Похоже, вам не хватает файла JAR, который содержит зависимость, как упоминалось в этой записи блога за июль 2006 года, написанной Санджив Дживан :

Разница между ClassNotFoundException и NoClassDefFoundError

A ClassNotFoundException генерируется, когда сообщаемый класс не найден загрузчиком классов.
Обычно это означает, что класс отсутствует в CLASSPATH.
Это также может означать, что рассматриваемый класс пытается загрузиться из другого класса, который был загружен в родительский элемент ClassLoader, и, следовательно, класс из дочернего элемента ClassLoader не отображается.
Это иногда имеет место при работе в более сложных средах, таких как сервер приложений (WebSphere печально известен такими ClassLoader проблемами).

Люди часто путают java.lang.NoClassDefFoundError с java.lang.ClassNotFoundException. Однако есть важное различие.

Например, исключение (ошибка действительно, так как java.lang.NoClassDefFoundError является подклассом java.lang.Error) как

java.lang.NoClassDefFoundError:
org/apache/activemq/ActiveMQConnectionFactory

не означает, что класс ActiveMQConnectionFactory отсутствует в CLASSPATH.

На самом деле, все наоборот.

Это означает, что класс ActiveMQConnectionFactory был найден ClassLoader, однако при попытке загрузить класс возникла ошибка при чтении определения класса.

Это обычно происходит, когда у рассматриваемого класса есть статические блоки или члены, которые используют класс, который не найден ClassLoader.

Таким образом, чтобы найти виновника, просмотрите исходный класс, о котором идет речь (в данном случае ActiveMQConnectionFactory), и ищите код, используя статические блоки или статические члены .
Если у вас нет доступа к источнику, просто декомпилируйте его, используя JAD .

При проверке кода, скажем, вы нашли строку кода, как показано ниже, убедитесь, что класс SomeClass входит в ваш CLASSPATH.

private static SomeClass foo = new SomeClass();

Совет. Чтобы узнать, к какому банку относится класс, вы можете воспользоваться веб-сайтом jarFinder . Это позволяет вам указать имя класса с использованием подстановочных знаков, и он ищет класс в своей базе данных jars.
jarhoo позволяет вам делать то же самое, но его больше нельзя использовать.

Если вы хотите определить, к какому jar-классу относится класс, по локальному пути, вы можете использовать утилиту, такую ​​как jarscan . Вы просто указываете класс, который хотите найти, и путь к корневому каталогу, в котором вы хотите начать поиск класса в jar-файлах и zip-файлах.

2 голосов
/ 02 марта 2009

Чтобы лучше справиться с проблемой загрузчика классов, просмотрите код, в котором он работает, и добавьте:

try
{
    final Class       clazz;
    final ClassLoader loader;

    clazz  = Class.forName("org/eclipse/jface/resource/FontRegistry");
    loader = clazz.getClassLoader(); 
    System.out.println("The classloader at step 1 is: " + loader);
}
catch(final Throwable ex)
{
    ex.printStackTrace();
}

И затем сделайте то же самое, когда получаете NoClassDefFoundError, и посмотрите, отличаются ли загрузчики классов.

Тогда вы сможете убедиться, что это ClassLoader, который отличается. Можете ли вы сообщить, что происходит с этим? В зависимости от результата у меня может быть больше идей.

2 голосов
/ 28 февраля 2009

Я думаю, что приведенная выше трассировка стека скрывает реальную проблему. Ниже приведен код в методе run в пределах org.eclipse.jface.dialogs.ProgressMonitorDialog (с добавленным мной комментарием):

public void run(boolean fork, boolean cancelable,
         IRunnableWithProgress runnable) throws InvocationTargetException,
         InterruptedException {
     setCancelable(cancelable);
     try {
         aboutToRun();
         // Let the progress monitor know if they need to update in UI Thread
         progressMonitor.forked = fork;
         ModalContext.run(runnable, fork, getProgressMonitor(), getShell()
                 .getDisplay());
     } finally {
         finishedRun();  // this is line 498
     }
}

Вторая строка снизу в трассировке стека Джареда - это строка 498 этого класса, которая является вызовом finishedRun() в блоке finally. Я подозреваю, что настоящей причиной является исключение, которое выдается в блоке try. Поскольку код в блоке finally также генерирует исключение, исходное исключение теряется.

1 голос
/ 20 сентября 2011

Если вы попытаетесь загрузить класс FontRegistry самостоятельно (как описано в TofoBeer), вы обнаружите, что классы следующего JAR являются зависимыми классами при использовании FontRegistry.

org.eclipse.core.commands_xxxxx.jar

Вы должны добавить этот JAR к вашему пути сборки.

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

Чтобы добавить к превосходному ответу ТофуБира , поскольку NoClassDefFoundError означает, что:

  • класс org.eclipse.jface.resource.FontRegistry был найден ClassLoader,
  • , но не может быть загружен без появления ошибки, например, наличие статических блоков или элементов, которые используют Class, который не найден ClassLoader.

Давайте посмотрим на org.eclipse.jface.resource.FontRegistry исходный код :

Он не имеет инициализации статической переменной (равно как и его суперклассы).

Давайте посмотрим на org.eclipse.jface.resource.JFaceResources исходный код

Функция getFontRegistry(), в которой вызывается ошибка, использует статическую переменную fontRegistry :

/**
 * The JFace font registry; <code>null</code> until lazily initialized or
 * explicitly set.
 */
private static FontRegistry fontRegistry = null;

Таким образом, просит поднимает вопрос : почему статическая инициализированная переменная внезапно снова будет считаться null?

Потому что каким-то образом FontRegistry или JFaceResources выгружается gc ?!

Если поле объявлено как статическое, существует ровно одно воплощение поля, независимо от того, сколько экземпляров (возможно, ноль) класса может быть в конечном итоге создано. Статическое поле, иногда называемое переменной класса, воплощается при инициализации класса (§12.4).

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


Если бы это был плагин eclipse , это могло бы быть связано с этой записью FAQ

Вот типичный сценарий для нового пользователя:
Вы пишете плагин, расширяющий плагин XYZ.
Чтобы его скомпилировать, вы добавляете ссылку на файл JAR для подключаемого модуля XYZ в путь сборки вашего проекта либо со страницы свойств пути сборки Java, либо путем редактирования файла .classpath.
При запуске рабочей среды выполнения появляется следующая неожиданная ошибка: java.lang.NoClassDefFoundError: XYZ.SomeClass.

Не начинайте искать на вкладке «Плагины и фрагменты» в конфигурации запуска рабочей среды времени выполнения.
Эта вкладка влияет только на то, какие подключаемые модули используются для вашей рабочей среды времени выполнения и загружаются ли они из рабочей области или из установочного каталога Eclipse.

Вместо этого начните искать в манифесте плагина.
Отредактируйте файл plugin.xml и убедитесь, что XYZ указан как необходимый плагин .
Затем сохраните файл plugin.xml.
Это автоматически обновит путь сборки проекта.

Никогда не редактируйте вручную файл .classpath при написании плагина.
Плагин Manifest Editor просто перезаписывает любые изменения, которые вы вносите в него. Не очень цивилизованно, но так оно и работает.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...