Могу ли я динамически выгружать и перезагружать (другие версии того же самого) JAR?

Я пишу серверную программу, которая используется для запуска модульных тестов API (отображение большого количества информации и обеспечение доступа в Интернет для управления /контролировать все это)...

Этот API известен серверу во время компиляции и предоставляется как JAR.

Чтобы иметь возможность сравнивать результаты unit test разных версий API (без перезапуска сервера), Я хочу иметь возможность выгрузить "текущую" версию API, и перезагрузить более новую (или более старую).

Я не хочу использовать URLClassLoader и вызывать каждый метод по названию
(используя getDeclaredMethod("someMethod")),
потому что сервер сильно зависит от API, и это будет сложно "обернуть" каждый вызов метода таким грязным способом.

Я думал: поскольку все интерфейсы всех версий JAR тот же, я не мог это сделать, перезагрузив другую версию из JAR (без этого имени-invokation?).

Примечание. Я использую последние Java SE (6) и Java EE (5).

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

Ответ 1

Я думаю, что если вы загружаете класс, используя

Class.forName(clsname, init, classloader); 

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

Пока вы очень осторожны с объектами, созданными с этого момента (для обеспечения GC), вы должны иметь возможность перезагружать разные версии. Я сделал это раньше с Java 1.3, потребовалось много отладки, но в конце у меня было приложение "bootstrap", которое загрузило класс Runnable по имени и смогло "перезагрузить", создав экземпляр нового загрузчика классов против другого URL-адреса и снова.

Ответ 2

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

  URLClassLoader urlClassLoader = (URLClassLoader) ClassLoader.getSystemClassLoader();
        Method m = URLClassLoader.class.getDeclaredMethod("addURL", new Class[]{URL.class});
        m.setAccessible(true);
        m.invoke(urlClassLoader, jarFile.toURI().toURL());
        String cp = System.getProperty("java.class.path");
        if (cp != null) {
            cp += File.pathSeparatorChar + jarFile.getCanonicalPath();
        } else {
            cp = jarFile.toURI().getPath();
        }
        System.setProperty("java.class.path", cp);

где jarFile - это версия jar, которую вы хотите использовать/перезаписать.

Ответ 3

Вы можете использовать пакет с открытым исходным кодом: JclLoader, который помогает при загрузке разных версий одной и той же банки. Это также необходимо в одной из наших систем для тестирования.

Ссылка: http://sourceforge.net/projects/jcloader/

Ответ 4

OSGi - это структура, которая позволит вам это сделать. JSR 277, система Java Module предназначена для этого (я думаю). Я не следовал дискуссиям OSGi -vs-JSR 277, поэтому я не знаю, что они вообще пытаются их вывести.

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

Ответ 5

Да. Я видел это на конференции NFJS. Это то, как такие вещи, как веб-контейнеры, поддерживают горячее развертывание приложений и предполагает использование возможностей классных загрузчиков. Для этого вам нужно создать новый загрузчик классов и использовать его для загрузки библиотеки, о которой идет речь. Затем выбросите загрузчика (или нет) и создайте другой, когда вы хотите перезагрузить. Возможно, вам также придется переопределить поведение загрузчика классов (я помню что-то о загрузчиках классов, получающих классы через их родителя по умолчанию.) Кроме того, я помню предупреждение о том, что объекты, созданные разными загрузчиками классов, не совместимые (не одного типа) друг с другом, даже если файл .class точно такой же.

В основном это глубокая магия.; -)

Ответ 6

Наверное, нет. Java-загрузчик классов не поддерживает загрузку во время выполнения; даже доступными загрузчиками классов являются хаки, которые используют прокси-объект.