如何编写可以在运行时进行自我更新的Java应用程序?


91

我想实现一个Java应用程序(服务器应用程序),该应用程序可以从给定的URL下载新版本(.jar文件),然后在运行时进行自身更新。

最好的方法是什么,有可能吗?

我猜该应用程序可以下载一个新的.jar文件并启动它。但是我应该如何进行切换,例如知道何时启动新应用程序然后退出。还是有更好的方法来做到这一点?


4
您是否看过Java Web Start?我相信,它不会在运行时进行自我更新(需要重新启动),因为您可能需要查看OSGi。
Thilo

@Thilo:我认为从给定的URL下载文件,然后从运行的jar文件中使用linux命令启动它很容易。
乔纳斯(Jonas)2010年

1
Java WebStart API的设计使其无法在运行时进行更新。不幸。
托尔比约恩Ravn的安徒生


@meain老板,你摇滚,你成就了我的一天:)
iltaf khalid

Answers:


68

解决方案的基本结构如下:

  • 有一个主循环负责重复加载应用程序的最新版本(如果需要)并启动它。

  • 该应用程序会执行其操作,但会定期检查下载URL。如果检测到新版本,它将返回启动器。

您可以通过多种方法来实现此目的。例如:

  • 启动器可以是包装器脚本或二进制应用程序,可以启动新的JVM来从被替换的JAR文件中运行该应用程序。

  • 启动器可以是Java应用程序,它为新的JAR创建类加载器,加载入口点类并在其上调用一些方法。如果以这种方式执行此操作,则必须注意类加载器存储泄漏,但这并不困难。(您只需要确保重新启动后,无法访问带有从JAR加载的类的对象即可。)

外部包装方法的优点是:

  • 您只需要一个JAR,
  • 您可以替换整个Java应用程序,
  • 该应用创建的所有辅助线程等都将在没有特殊关闭逻辑的情况下消失,并且
  • 您还可以处理从应用程序崩溃等中恢复。

第二种方法需要两个JAR,但具有以下优点:

  • 该解决方案是纯Java且可移植的,
  • 转换将更快,并且
  • 您可以在重新启动期间更轻松地保留状态(模泄漏问题)。

“最佳”方式取决于您的特定要求。

还应注意:

  • 自动更新存在安全风险。通常,如果提供更新的服务器受到损害,或者提供更新的机制容易受到攻击,则自动更新可能会导致客户端受到损害。

  • 向客户推送导致损坏客户的更新可能具有法律风险,也可能威胁到您的企业声誉。


如果您能找到避免重新发明轮子的方法,那将是很好的。请参阅其他答案以获取建议。


43

我目前正在开发JAVA Linux守护程序,也需要实现自动更新机制。我想将应用程序限制在一个jar文件中,并提出了一个简单的解决方案:

将更新程序应用程序打包在更新本身中。

应用程序:当应用程序检测到较新版本时,它将执行以下操作:

  1. 下载更新(Zipfile)
  2. 提取Application和ApplicationUpdater(均在zip文件中)
  3. 运行更新程序

应用更新器:运行更新程序时,它将执行以下操作:

  1. 停止应用程序(在我的情况下是通过init.d的守护程序)
  2. 复制下载的jar文件以覆盖当前的应用程序
  3. 启动应用程序
  4. 清理。

希望它能帮助某人。


12

这是一个已知的问题,我建议不要重新发明轮子-不要编写自己的hack,而要使用别人已经做过的事情。

您需要考虑两种情况:

  1. 应用必须是可自我更新的,并且即使在更新过程中也要保持运行(服务器应用,嵌入式应用)。使用OSGi:BundlesEquinox p2

  2. 应用程序是桌面应用程序,具有安装程序。有许多具有更新选项的安装程序。检查安装程序列表


12

我最近创建了与Java 9的模块系统完全兼容的update4j

它将无缝启动新版本,而无需重新启动。


通过使用引导程序/业务应用程序范例。引导程序将加载和卸载业务应用程序,并且引导程序通常执行更新。
Mordechai

10

我编写了一个Java应用程序,该程序可以在运行时加载插件并立即开始使用它们,这受到jEdit中类似机制的启发。jEdit是开源的,因此您可以选择查看其工作方式。

该解决方案使用自定义的ClassLoader从jar中加载文件。加载它们后,您可以从新的jar中调用某些方法作为其main方法。然后,棘手的部分是确保您摆脱对旧代码的所有引用,以便可以对其进行垃圾回收。我在这方面还不是专家,我已经做到了,但这并不容易。


我不了解Java,但是在C#中,您可以使用AppDomains显式卸载代码。也许Java中有类似的概念。
Merlyn Morgan-Graham

1
谢谢,这似乎是要走的路。有一个例子NetworkClassLoader在Java文档的ClassLoader
乔纳斯

您是否在同一JVM中遇到静态变量问题,或者永久生成该怎么办?我听说这些会带来有关类卸载的问题。
ide

5
  1. 第一种方法:使用tomcat及其部署工具。
  2. 第二种方法:将应用程序分为两个部分(功能部分和更新部分),然后让更新部分替换功能部分。
  3. 第三种方法:在服务器应用程序中,只需下载新版本,然后旧版本释放绑定端口,然后旧版本运行新版本(启动进程),然后旧版本在应用程序端口上向新版本发送请求以删除旧版本,旧版本终止,新版本删除旧版本。像这样: 替代文字

您的第二种方法似乎是一个有趣的设计。
乔纳斯(Jonas)2010年

2

这不一定是最好的方法,但它可能对您有用。

您可以编写引导程序(如果您玩过《魔兽世界》,则是《魔兽世界》启动器)。该引导程序负责检查更新。

  • 如果有更新,它将提供给用户,处理下载,安装等。
  • 如果应用程序是最新的,它将允许用户启动该应用程序
  • (可选)您可以允许用户启动该应用程序,即使它不是最新的

这样,您不必担心强制退出应用程序。

如果您的应用程序是基于Web的,并且重要的是它们具有最新的客户端,那么您还可以在应用程序运行时进行版本检查。您可以在与服务器执行正常通信(部分或全部呼叫)或同时执行两者时,每隔一段时间进行一次操作。

对于我最近使用的产品,我们在启动时(没有引导程序应用程序,但在出现主窗口之前)以及在调用服务器期间进行了版本检查。当客户端过期时,我们依靠用户手动退出,但禁止对服务器执行任何操作。

请注意,在打开主窗口之前,我不知道Java是否可以调用UI代码。我们正在使用C#/ WPF。


谢谢,这是一种很好的方法。但这是一个服务器应用程序,因此没有用户可以执行某些操作。而且我希望它只是一个jar文件,因此用户easyli可以下载一个jar并在开始时将其启动,但是之后,我希望没有用户交互。
乔纳斯

当您说“服务器应用程序”时,是否表示它是登录后直接在服务器上运行的应用程序?
Merlyn Morgan-Graham

@Jonas:我对.jar文件的工作方式了解不多,所以在那里我没有太多服务。我能理解您是否希望使用特定于Java的答案。希望这至少能给您带来深思的地方:)
Merlyn Morgan-Graham

@梅林:我很感激您的想法。是的,它是一个将在Linux服务器上运行的Java应用程序,该服务器上没有人登录。
乔纳斯(Jonas)2010年

1
@Jonas:不是您还没有考虑过,但是-我见过的大多数Web服务都有手动部署策略。您可能要谨慎检查更新。如果您增加了错误的代码/损坏的代码,或者仅通过多文件部署完成了部分工作,服务器可能会尝试自行更新,然后您的服务器就会损坏。
Merlyn Morgan-Graham


1

下载新的jar(例如,一名中级攻击者)时,我看到一个安全问题。您始终必须签署可下载的更新。

在JAX2015上,Adam Bien讲述了如何使用JGit更新二进制文件。可悲的是我找不到任何教程。

资料来源为德文。

Adam Bien创建了更新程序,请参见此处

在这里用了一些javaFX前端。我也在研究自动签名。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.