2014-10-02 2 views
5

Резюме:Загрузка банки из работающей программы Java вызывает NoClassDefFoundError вызванной ClassNotFoundException, вызванной зависимостями между классом (например import заявлений). Как я могу обойти это?Загрузка банки во время выполнения вызывает NoClassDefFoundError/ClassNotFoundException

Проблема более подробно:

Я пытаюсь программно загрузить файл банку - давайте назовем его «Сервер» - в виртуальной машине Java через мою собственную программу Java - давайте вызов это «ServerAPI» - и использовать расширение и некоторые другие трюки для изменения поведения и взаимодействия с Сервером. ServerAPI зависит от сервера, но если сервер отсутствует, ServerAPI по-прежнему должен иметь возможность запускать и загружать сервер с веб-сайта.

Чтобы избежать ошибок, вызванных загрузкой ServerAPI без удовлетворения его зависимостей от Сервера, я создал программу запуска - назовем ее «Launcher» - это значит, что необходимо загрузить Сервер и настроить ServerAPI по необходимости, а затем загрузить Сервер и ServerAPI, затем запустите ServerAPI.

Однако, когда я пытаюсь загрузить баночки из Launcher, я получаю ошибки, вызванные тем, что ClassLoaders не могут разрешить другие классы в файле, от которого зависит его класс. Короче говоря, если я попытаюсь загрузить класс A, он выдаст ошибку, если A импортирует B, потому что я еще не загрузил B. Однако, если B также импортирует A, я застреваю, потому что не могу понять, как загрузить сразу два класса или как загрузить класс без проверки JVM.

Почему все ограничения привели меня к этой проблеме:

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

Однако, по более сложным юридическим причинам, Server и ServerAPI не могут быть просто загружены вместе. Launcher (см. Выше) должен быть загружен с ServerAPI, тогда Launcher необходимо загрузить Server. Наконец, ServerAPI может быть запущен с использованием Server в качестве зависимости. Вот почему эта проблема настолько сложна.

Эта проблема также применима к более поздней части проекта, которая будет включать интерфейс API на основе плагинов, который должен иметь возможность загружать и выгружать плагины из файлов jar во время работы.

исследований я уже сделал по этой проблеме:

Я прочитал и не быть оказана помощь:

  • this question, который только рассматривается вопрос о единой методике и не адресные ошибки межклассовой зависимости;
  • this question, который не будет работать, потому что я не могу выключать и перезапускать программу каждый раз, когда ящик загружается или выгружается (в основном для части плагина, о которой я кратко упомянул);
  • this question, который работает только в ситуациях, когда зависимости присутствуют при запуске программы;
  • this question, который имеет такую ​​же проблему, как # 2;
  • this question, что имеет ту же проблему, что и # 3;
  • this article, из которого я узнал о скрытом loadClass (String, Boolean) метод, но пытаюсь с истинных и ложных значений не помог;
  • this question, который имеет ту же проблему, что и # 1;

и больше. Ничего не сработало.

// EDIT: попытки я сделал до сих пор:

Я попытался с помощью URLClassLoaders, чтобы загрузить банку с помощью JarEntries из JarFile подобной this question. Я пробовал это как с помощью метода, так и с помощью класса, который расширяет URLClassLoader, так что я мог бы использовать loadClass(String, boolean resolve), чтобы попытаться заставить ClassLoader решить все классы, которые он загружает. Оба способа, я получил такую ​​же ошибку:

I couldn't find the class in the JarEntry! 
entry name="org/apache/logging/log4j/core/appender/db/jpa/converter/ContextMapAttributeConverter.class" 
class name="org.apache.logging.log4j.core.appender.db.jpa.converter.ContextMapAttributeConverter" 
java.lang.NoClassDefFoundError: javax/persistence/AttributeConverter 
    at java.lang.ClassLoader.defineClass1(Native Method) 
    at java.lang.ClassLoader.defineClass(ClassLoader.java:760) 
    at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:142) 
    at java.net.URLClassLoader.defineClass(URLClassLoader.java:455) 
    at java.net.URLClassLoader.access$100(URLClassLoader.java:73) 
    at java.net.URLClassLoader$1.run(URLClassLoader.java:367) 
    at java.net.URLClassLoader$1.run(URLClassLoader.java:361) 
    at java.security.AccessController.doPrivileged(Native Method) 
    at java.net.URLClassLoader.findClass(URLClassLoader.java:360) 
    at java.lang.ClassLoader.loadClass(ClassLoader.java:424) 
    at Corundum.launcher.CorundumClassLoader.load(CorundumClassLoader.java:52) 
    at Corundum.launcher.CorundumLauncher.main(CorundumLauncher.java:47) 
Caused by: java.lang.ClassNotFoundException: javax.persistence.AttributeConverter 
    at java.net.URLClassLoader$1.run(URLClassLoader.java:372) 
    at java.net.URLClassLoader$1.run(URLClassLoader.java:361) 
    at java.security.AccessController.doPrivileged(Native Method) 
    at java.net.URLClassLoader.findClass(URLClassLoader.java:360) 
    at java.lang.ClassLoader.loadClass(ClassLoader.java:424) 
    at java.lang.ClassLoader.loadClass(ClassLoader.java:357) 
    ... 12 more 

// END EDIT

// EDIT 2:

Вот пример кода, который я использовал для загрузки класса, пытаясь его решить. Это было внутри класса, который я сделал, который расширяет URLClassLoader. В строке, начинающейся с Class<?> clazz = loadClass(, я попытался использовать true и false как логический аргумент; обе попытки привели к той же ошибке выше.

public boolean load(ClassLoadAction class_action, FinishLoadAction end_action) { 
    // establish the jar associated with this ClassLoader as a JarFile 
    JarFile jar; 
    try { 
     jar = new JarFile(jar_path); 
    } catch (IOException exception) { 
     System.out.println("There was a problem loading the " + jar_path + "!"); 
     exception.printStackTrace(); 
     return false; 
    } 

    // load each class in the JarFile through its JarEntries 
    Enumeration<JarEntry> entries = jar.entries(); 

    if (entries.hasMoreElements()) 
     for (JarEntry entry = entries.nextElement(); entries.hasMoreElements(); entry = entries.nextElement()) 
      if (!entry.isDirectory() && entry.getName().endsWith(".class")) 
       try { 
        /* this "true" in the line below is the whole reason this class is necessary; it makes the URLClassLoader this class extends "resolve" the class, 
        * meaning it also loads all the classes this class refers to */ 
        Class<?> clazz = loadClass(entry.getName().substring(0, entry.getName().length() - 6).replaceAll("/", "."), true); 
        class_action.onClassLoad(this, jar, clazz, end_action); 
       } catch (ClassNotFoundException | NoClassDefFoundError exception) { 
        try { 
         close(); 
        } catch (IOException exception2) { 
         System.out.println("There was a problem closing the URLClassLoader after the following " + exception2.getClass().getSimpleName() + "!"); 
         exception.printStackTrace(); 
        } 
        try { 
         jar.close(); 
        } catch (IOException exception2) { 
         System.out.println("There was a problem closing the JarFile after the following ClassNotFoundException!"); 
         exception.printStackTrace(); 
        } 
        System.out.println("I couldn't find the class in the JarEntry!\nentry name=\"" + entry.getName() + "\"\nclass name=\"" 
          + entry.getName().substring(0, entry.getName().length() - 6).replaceAll("/", ".") + "\""); 
        exception.printStackTrace(); 
        return false; 
       } 

    // once all the classes are loaded, close the ClassLoader and run the plugin's main class(es) load() method(s) 
    try { 
     jar.close(); 
    } catch (IOException exception) { 
     System.out.println("I couldn't close the URLClassLoader used to load this jar file!\njar file=\"" + jar.getName() + "\""); 
     exception.printStackTrace(); 
     return false; 
    } 

    end_action.onFinishLoad(this, null, class_action); 
    System.out.println("loaded " + jar_path); 
    // TODO TEST 
    try { 
     close(); 
    } catch (IOException exception) { 
     System.out.println("I couldn't close the URLClassLoader used to load this jar file!\njar file=\"" + jar_path + "\""); 
     exception.printStackTrace(); 
     return false; 
    } 
    return true; 
} 

// END EDIT 2

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

+0

Вы подтвердили из трассировки стека, что это фактически сам импорт, вызывающий проблему, а не (скажем) непреднамеренный доступ другого класса из конструктора или статической переменной? (И если это действительно импорт, что произойдет, если вы удалите импорт и полностью квалифицируете все ссылки на объекты для класса?) Наконец, можете ли вы опубликовать, как вы загружаете банку? –

+0

В таких случаях может быть полезно продумать очень простой пример с двумя классами без ничего в отдельных банках и выяснить, есть ли у них такая же проблема при их загрузке. Если нет, продолжайте увеличивать сложность модели, пока она не отразится на шаблонах, используемых в вашем реальном приложении. Например, я заметил (в среде OSGi), что загрузчик классов любит разрешать все классы, которые так или иначе ссылаются на класс, который я загружаю напрямую, но он не пытается разрешить классы, на которые ссылаются в одном -step-remove классы до тех пор, пока они не будут вызваны. –

+0

(В этом случае добавить класс прокладок между исходным и целевым классами было достаточно, чтобы сделать загрузчик классов счастливым, если класс прокладок никогда не запускается до загрузки целевого класса.) –

ответ

2

Взволнованно, я обнаружил, что ответ был тот, что сообщение об ошибке говорило правду.javax.persistence.AttributeConverter, класс, который требовал загрузчик, отсутствовал, не было в банке.

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

Теперь я мог бы поклясться, что я проверил это раньше и нашел этот класс; Я полагаю, что я должен был проверить репозиторий с открытым исходным кодом Apache для класса, а не фактический сервер, когда я его проверил. Я не помню.

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

Извините, что потерял время каждого ... включая мои собственные. Некоторое время я застрял в этой проблеме.

Мораль этой истории:

Если вы пытаетесь загрузить исполняемый банку, не беспокоить загрузки всех классов в банке, если вы на самом деле не нужно. Просто загрузите основной класс; который загрузит все, что программа должна выполнить.

// EDIT:

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

Смежные вопросы