Как я могу написать приложение Java, которое может обновляться во время выполнения?

91

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

Как лучше всего это сделать и возможно ли это?

Я предполагаю, что приложение может загрузить новый файл .jar и запустить его. Но как мне выполнить передачу, например, узнать, когда новое приложение запущено, а затем выйти. Или есть способ лучше?

Йонас
источник
4
Вы смотрели Java Web Start? Хотя я считаю, что он не обновляется во время выполнения (требуется перезагрузка), для этого вам, вероятно, нужно взглянуть на OSGi.
Тило
@Thilo: Я думаю, было бы легко загрузить файл с заданного URL-адреса, а затем запустить его с помощью команды linux из запущенного файла jar.
Jonas
1
Дизайн Java WebStart API делает невозможным обновление во время работы. К сожалению.
Торбьёрн Равн Андерсен,
@meain boss you rock, you made my day :)
iltaf khalid

Ответы:

68

Базовая структура решения следующая:

  • Существует основной цикл, отвечающий за многократную загрузку последней версии приложения (при необходимости) и ее запуск.

  • Приложение делает свое дело, но периодически проверяет URL загрузки. Если он обнаруживает новую версию, он возвращается в программу запуска.

Есть несколько способов реализовать это. Например:

  • Средство запуска может быть сценарием-оболочкой или двоичным приложением, которое запускает новую JVM для запуска приложения из заменяемого файла JAR.

  • Средство запуска может быть приложением Java, которое создает загрузчик классов для нового JAR, загружает класс точки входа и вызывает для него какой-то метод. Если вы сделаете это таким образом, вам придется следить за утечками хранилища загрузчика классов, но это несложно. (Вам просто нужно убедиться, что никакие объекты с классами, загруженными из JAR, не доступны после перезапуска.)

Преимущества подхода внешней оболочки:

  • вам нужен только один JAR,
  • вы можете заменить все приложение Java,
  • любые вторичные потоки, созданные приложением и т. д., будут удалены без специальной логики завершения работы, и
  • также можно заниматься восстановлением после сбоев приложений и т. д.

Второй подход требует двух JAR, но имеет следующие преимущества:

  • решение - чистая Java и портативное,
  • переключение будет быстрее, и
  • вы можете легче сохранять состояние при перезапуске (проблемы с утечкой по модулю).

«Лучший» способ зависит от ваших конкретных требований.

Также следует отметить, что:

  • При автоматическом обновлении есть риски безопасности. В общем, если сервер, предоставляющий обновления, скомпрометирован или если механизмы предоставления обновлений уязвимы для атак, то автоматическое обновление может привести к компрометации клиента (ов).

  • Отправка клиенту обновления, которое причиняет ущерб клиенту, может иметь юридические риски и риски для репутации вашего бизнеса.


Если вы найдете способ не изобретать велосипед заново, это будет хорошо. См. Другие ответы для предложений.

Стивен С
источник
43

В настоящее время я разрабатываю JAVA Linux Daemon, и мне также нужно было реализовать механизм автоматического обновления. Я хотел ограничить свое приложение одним файлом jar и нашел простое решение:

Упакуйте программу обновления в само обновление.

Приложение : когда приложение обнаруживает более новую версию, оно выполняет следующие действия:

  1. Загрузить обновление (Zip-файл)
  2. Распакуйте приложение и ApplicationUpdater (все в zip-файле)
  3. Запустить программу обновления

ApplicationUpdater : при запуске программа обновления выполняет следующие действия:

  1. Остановите приложение (в моем случае демон через init.d)
  2. Скопируйте загруженный файл jar, чтобы перезаписать текущее приложение
  3. Запустить приложение
  4. Очистка.

Надеюсь, это кому-то поможет.

Бердус
источник
12

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

Вам необходимо рассмотреть две ситуации:

  1. Приложение должно быть самообновляемым и продолжать работать даже во время обновления (серверное приложение, встроенные приложения). Используйте OSGi: Bundles или Equinox p2 .

  2. Приложение является настольным приложением и имеет установщик. Есть много установщиков с возможностью обновления. Проверить список установщиков .

Петр Кнего
источник
12

Недавно я создал update4j, который полностью совместим с модульной системой Java 9.

Новая версия будет запущена без перезагрузки.

Мордехай
источник
Используя парадигму начального / бизнес-приложения. Загрузочная программа загружает и выгружает бизнес-приложение, и обычно она выполняет обновление.
Мордехай
10

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

Решение использует собственный ClassLoader для загрузки файлов из jar. Как только они загружены, вы можете вызвать какой-нибудь метод из нового jar-файла, который будет действовать как его mainметод. Затем сложнее всего убедиться, что вы избавились от всех ссылок на старый код, чтобы его можно было собрать мусором. Я не совсем специалист в этой части, я заставил это работать, но это было непросто.

Брэд Мейс
источник
Я не знаю о Java, но в C # вы можете использовать AppDomains для явной выгрузки кода. Может быть, в Java есть похожая концепция.
Мерлин Морган-Грэм,
1
Спасибо, похоже, это верный путь. В NetworkClassLoaderJavaDoc есть пример для ClassLoader
Джонас
У вас возникли проблемы со статическими переменными в той же JVM или как насчет постоянной генерации? Я слышал, что это может вызвать проблемы с разгрузкой класса.
ide
5
  1. Первый способ: используйте tomcat и его средства развертывания.
  2. Второй способ: разделить приложение на две части (функциональную и обновляемую) и позволить обновленной части заменить функциональную часть.
  3. Третий способ: в вашем серверном приложении просто загрузите новую версию, затем старая версия освобождает связанный порт, затем старая версия запускает новую версию (запускает процесс), затем старая версия отправляет запрос на порт приложения в новую версию для удаления старой версии, старой версии завершается, и новая версия удаляет старую версию. Как это: альтернативный текст
младший
источник
Ваш второй способ кажется интересным.
Йонас,
2

Это не обязательно лучший способ, но он может сработать для вас.

Вы можете написать приложение для начальной загрузки (как лаунчер World of Warcraft, если вы играли в WoW). Этот бутстрап отвечает за проверку обновлений.

  • Если обновление доступно, оно предложит его пользователю, выполнит загрузку, установку и т. Д.
  • Если приложение обновлено, это позволит пользователю запустить приложение.
  • При желании вы можете разрешить пользователю запускать приложение, даже если оно не обновлено.

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

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

Для продукта, над которым я недавно работал, мы выполняли проверку версии при запуске (без приложения загрузочного ремня, но до появления главного окна) и во время обращений к серверу. Когда клиент устарел, мы полагались на то, что пользователь завершит работу вручную, но запрещали любые действия против сервера.

Обратите внимание, что я не знаю, может ли Java вызывать код пользовательского интерфейса до того, как вы откроете главное окно. Мы использовали C # / WPF.

Мерлин Морган-Грэм
источник
Спасибо, это хороший способ сделать это. Но это серверное приложение, поэтому нет пользователя, который мог бы что-то предпринять. И я бы предпочел, чтобы это был только один файл jar, чтобы пользователь easyli мог загрузить один файл jar и запустить его с самого начала, но после этого я бы хотел, чтобы пользователь не взаимодействовал.
Jonas
Когда вы говорите «серверное приложение», вы имеете в виду, что это приложение, которое запускается непосредственно на сервере при входе в систему?
Мерлин Морган-Грэм,
@Jonas: Я не очень разбираюсь в том, как работают файлы .jar, поэтому я не могу там много помочь. Я могу понять, предпочитаете ли вы ответ, относящийся к Java. Надеюсь, это даст вам пищу для размышлений, по крайней мере :)
Мерлин Морган-Грэм,
@Merlyn: Я благодарен за то, что поделился вашими идеями. Да, это приложение Java, которое будет работать на сервере Linux, и никто не авторизован на этом сервере.
Jonas
1
@Jonas: Не то чтобы вы об этом еще не думали, но - Большинство веб-сервисов, которые я видел, имеют стратегию развертывания вручную. Вы можете быть осторожны при проверке обновлений. Если вы запустите ошибочный / сломанный код или выполните только часть работы с многофайловым развертыванием, сервер может попытаться обновить себя, и тогда у вас будет сломанный сервер.
Мерлин Морган-Грэм,
2

Если вы создаете свое приложение с использованием плагинов Equinox , вы можете использовать P2 Provisioning System, чтобы получить готовое решение этой проблемы. Это потребует перезапуска сервера после обновления.

Том Крокетт
источник
1

Я вижу проблему безопасности при загрузке нового jar-файла (и т. Д.), Например, атака человека посередине. Вы всегда должны подписывать загружаемое обновление.

На JAX2015 Адам Бьен рассказал об использовании JGit для обновления двоичных файлов. К сожалению, я не смог найти никаких руководств.

Источник на немецком языке.

Адам Бьен создал программу обновления см. Здесь

Я раздвоил это раздвоил здесь с помощью внешнего интерфейса javaFX. Я также работаю над автоматической подписью.

Kaito
источник