Обновление JAR во время работы - PullRequest
23 голосов
/ 25 июня 2011

Учитывая, что jar работает в JVM, можно было бы выгрузить текущий запущенный Jar и удалить его из системы.Загрузите новую версию и переименуйте ее с тем же именем последнего Jar, а затем инициализируйте новый Jar, создавая бесшовное обновление Jar в JVM.Можно ли даже поручить JVM выполнить это действие?Можно ли обновить Jar во время его работы?

Ответы [ 7 ]

25 голосов
/ 25 июня 2011

Загрузите новую версию и переименуйте ее с тем же именем последнего Jar, а затем инициализируйте новый Jar, создав плавное обновление Jar в JVM ... Возможно ли даже обновить Jar, покаон работает?

Файл JAR не «работает», JVM работает.Ваш JAR-файл просто содержит информацию о классе (или инструкции байт-кода), которая делает JVM полезной работой.В большинстве случаев JVM на самом деле не устанавливает системную блокировку вашего файла JAR, поэтому вы можете заменить этот файл содержимым своего сердца.

Реальная проблема, конечно, заключается в том, что как только JVM загрузит ваш JAR, он будет нестивместе с тем, что он загружал и никогда не читал из вашего файла JAR снова, независимо от того, сколько раз вы перезаписывали его.Это поведение загрузчика классов по умолчанию и не может быть изменено - однако, как отмечали другие, - вам НЕ обязательно использовать загрузчик классов по умолчанию.Вы можете реализовать свой собственный, аналогичный тому, который используют серверы веб-приложений, для загрузки обновленных JARS из файловой системы.Однако будьте осторожны - определение вашего собственного загрузчика классов считается «плохой идеей ™», если вы действительно не знаете, что делаете.Вы можете прочитать больше здесь и здесь .

24 голосов
/ 02 апреля 2014

Это то, что я видел, много раз делал раньше (и тоже делал сам). Я сформулировал некоторые моменты проблем / решений, которые могут возникнуть.

  • JVM будет аварийно завершать работу с дампом, если вы перезапишете файл JAR, который он будет использовать позже.
    • Позже я имею в виду, что классы загружаются довольно лениво, а некоторые могут быть загружены только позже в жизни вашей программы
    • JVM имеет открытый дескриптор для файла JAR, и библиотека не будет работать, поскольку JAR и указатели станут неправильными
    • Вероятность можно уменьшить, предварительно загрузив все классы и ресурсы из файла JAR
    • Если у вас есть собственный загрузчик классов, вы можете сами закрыть его.
  • Вам нужно будет знать, как осуществляется загрузка классов. Лучше все же быть под контролем.
    • Пользовательский загрузчик классов, который будет создавать загрузчики классов для каждого JAR и управлять версиями
    • Узнайте, как ваше приложение использует загрузчики классов и как оно работает с новыми JAR-файлами (например, проверьте, что делает Tomcat при перезаписи архива WAR)
  • В Windows ваши JAR-файлы будут заблокированы, и вы не сможете их перезаписать. Если у вас все под контролем, вы можете разблокировать их после использования (закрыть их). Для сторонних систем вы должны найти соответствующие флаги. Например, вы можете проверить antiJARLocking в конфигурации контекста Tomcat.
  • Всегда лучше избегать перезаписи одного и того же файла, а вместо этого делать некоторые версии версий

В общем, есть много проблем, с которыми вы можете столкнуться, когда хотите добиться перезагрузки JAR. К счастью, есть способы, как минимизировать риски. Самый безопасный способ - сделать что-то похожее, чтобы получить тот же эффект. Cleanest - это пользовательские загрузчики классов и управление версиями файлов JAR.

1 голос
/ 25 июня 2011

Ответ лежит в Java Class Loaders.Эти ребята загружают классы из файлов JARS, или .class, или значения byte[], или URL, или чего-то еще.Всякий раз, когда вы обращаетесь к классу, вы неявно используете загрузчик классов, чтобы предоставить вам правильный экземпляр класса.

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

Определение собственного загрузчика классов очень просто - просто создайте подкласс класса ClassLoader и переопределите его метод findClass .

1 голос
/ 25 июня 2011

Вы не можете написать на работающую банку. Нет эквивалента getResourceInputStream для записи. Я предполагаю, что если вы попытаетесь написать с использованием FileOutputStream, поскольку JVM использует его, вы не сможете удалить его, поскольку система предотвратит его.

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

Также возможно использовать JNLP для автоматического и плавного обновления приложения.

Серверные приложения также являются альтернативой для скрытия обновлений для пользователя.

С уважением, Stéphane

1 голос
/ 25 июня 2011

Как правило, вы не можете сделать это, поскольку, насколько мне известно, это поведение не определено официально.

Однако вы МОЖЕТЕ создать загрузчик классов, используя файл jar вне вашего официального пути к классам, а затем загрузить классы изэто как вам нужно.Отбросив все экземпляры классов, загруженных загрузчиком классов, вы можете удалить текущие ресурсы, а затем создать новый загрузчик классов в новом файле jar, а затем загрузить новые классы и создать новые объекты.

Это довольно сложно, так что, возможно,вместо этого вы бы сделали jar модулем OSGi и вызывали свою программу через OSGi-загрузчик?

0 голосов
/ 26 октября 2018

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

byte[] file = recieve();

FileOutputStream fos = new FileOutputStream("software.jar");
fos.flush(); fos.write(file); fos.close();

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

Я не указал точно, что может быть или не быть ошибкой, однако в некоторых случаях я наблюдал сбой с hs_err_pid.log.

У меня также есть переменная final и static, которая называется SOFTWARE_VERSION.Я подтвердил, что эта переменная обновляется (при наблюдении через интерфейс сервера) без перезагрузки программного обеспечения после его замены.

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

Runtime.getRuntime().exec("sudo java -jar software.jar");
System.exit(0);

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

Runtime.getRuntime().exec("run_software.sh")
System.exit(0);

Затем в run_software.sh:

sleep 1000
sudo java -jar software.jar

Мне было бы интересно узнать, будет ли это работать.

Надеюсь, это поможет!

0 голосов
/ 04 сентября 2018

Вы можете использовать HotswapAgent для достижения этой цели.Он поддерживает плагины для нескольких широко используемых фреймворков, а также облегчает написание новых пользовательских плагинов

HotswapAgent - https://github.com/HotswapProjects/HotswapAgent

...