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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/4002462/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-14 08:11:07  来源:igfitidea点击:

How can I write a Java application that can update itself at runtime?

javajarauto-updateserver-application

提问by Jonas

I would like to implement a java application (server application) that can download a new version (.jar file) from a given url, and then update itself at runtime.

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

What is the best way to do this and is it possible?

做到这一点的最佳方法是什么,是否可能?

I guess that the application can download a new .jar file and start it. But how should I do the handover, e.g. know when the new application is started and then exit. Or is there a better way to do this?

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

采纳答案by Stephen C

The basic structure of a solution is as follows:

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

  • There is a main loop responsible for repeatedly loading the latest version of the app (if required) and launching it.

  • The application does its thing, but periodically checks the download URL. If it detects a new version it exits back to the launcher.

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

  • 应用程序完成它的工作,但会定期检查下载 URL。如果它检测到新版本,它会返回到启动器。

There are a number of ways you could implement this. For example:

有多种方法可以实现这一点。例如:

  • The launcher could be a wrapper script or binary application that starts a new JVM to run the application from a JAR file that gets replaced.

  • The launcher could be a Java application that creates a classloader for the new JAR, loads an entrypoint class and calls some method on it. If you do it this way, you have to watch for classloader storage leaks, but that's not difficult. (You just need to make sure that no objects with classes loaded from the JAR are reachable after you relaunch.)

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

  • 启动器可以是一个 Java 应用程序,它为新的 JAR 创建一个类加载器,加载一个入口点类并在它上面调用一些方法。如果你这样做,你必须注意类加载器存储泄漏,但这并不困难。(您只需要确保在重新启动后无法访问具有从 JAR 加载的类的对象。)

The advantages of the external wrapper approach are:

外部包装方法的优点是:

  • you only need one JAR,
  • you can replace the entire Java app,
  • any secondary threads created by the app, etc will go away without special shutdown logic, and
  • you can also deal with recovery from application crashes, etc.
  • 你只需要一个 JAR,
  • 您可以替换整个 Java 应用程序,
  • 应用程序创建的任何辅助线程等都将在没有特殊关闭逻辑的情况下消失,并且
  • 您还可以处理从应用程序崩溃等中恢复的问题。

The second approach requires two JARs, but has the following advantages:

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

  • the solution is pure Java and portable,
  • the changeover will be quicker, and
  • you can more easily retain state across the restart (modulo leakage issues).
  • 解决方案是纯 Java 和可移植的,
  • 转换会更快,并且
  • 您可以更轻松地在重启期间保持状态(模泄漏问题)。

The "best" way depends on your specific requirements.

“最佳”方式取决于您的具体要求。

It should also be noted that:

还应注意的是:

  • There are security risks with auto-updating. In general, if the server that provides the updates is compromised, or if the mechanisms for providing the updates are susceptible to attack, then auto-updating can lead to a compromise of the client(s).

  • Pushing a update to a client that cause damage to the client could have legal risks, and risks to your business' reputation.

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

  • 向客户推送更新会对客户造成损害可能会带来法律风险,并对您的企业声誉造成风险。



If you can find a way to avoid reinventing the wheel, that would be good. See the other answers for suggestions.

如果你能找到一种方法来避免重新发明轮子,那就太好了。请参阅其他答案以获取建议。

回答by Merlyn Morgan-Graham

This isn't necessarily the bestway, but it might work for you.

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

You can write a bootstrap application (ala the World of Warcraft launcher, if you've played WoW). That bootstrap is responsible for checking for updates.

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

  • If an update is available, it will offer it to the user, handle the download, installation, etc.
  • If the application is up to date, it will allow the user to launch the application
  • Optionally, you can allow the user to launch the application, even if it isn't up to date
  • 如果有可用更新,它将提供给用户,处理下载、安装等。
  • 如果应用程序是最新的,它将允许用户启动应用程序
  • 或者,您可以允许用户启动应用程序,即使它不是最新的

This way you don't have to worry about forcing an exit of your application.

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

If your application is web based, and if it is important that they have an up to date client, then you can also do version checks while the application runs. You can do them at intervals, while performing normal communication with the server (some or all calls), or both.

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

For a product I recently worked on, we did version checks upon launch (without a boot strapper app, but before the main window appeared), and during calls to the server. When the client was out of date, we relied on the user to quit manually, but forbid any action against the server.

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

Please note that I don't know if Java can invoke UI code before you bring up your main window. We were using C#/WPF.

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

回答by Brad Mace

I've written a Java application that can load plugins at runtime and start using them immediately, inspired by a similar mechanism in jEdit. jEdit is open source so you have the option of looking to see how it works.

我编写了一个 Java 应用程序,它可以在运行时加载插件并立即开始使用它们,灵感来自 jEdit 中的类似机制。jEdit 是开源的,因此您可以选择查看它是如何工作的。

The solution uses a custom ClassLoader to load files from the jar. Once they're loaded you can invoke some method from the new jar that will act as its mainmethod. Then the tricky part is making sure you get rid of all references to the old code so that it can be garbage collected. I'm not quite an expert on that part, I've made it work but it wasn't easy.

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

回答by jnr

  1. First way: use tomcat and it's deploy facilities.
  2. Second way: to split application on two parts (functional and update) and let update part replace function part.
  3. Third way: In your server appliction just download new version, then old version releases bound port, then old version runs new version (starts process), then old version sends a request on application port to the new version to delete old version, old version terminates and new version deletes old version. Like this: alt text
  1. 第一种方式:使用 tomcat 和它的部署设施。
  2. 第二种方式:将应用程序拆分为两部分(功能和更新),让更新部分替换功能部分。
  3. 第三种方式:在您的服务器应用程序中下载新版本,然后旧版本释放绑定端口,然后旧版本运行新版本(启动进程),然后旧版本在应用程序端口上向新版本发送请求删除旧版本,旧版本终止,新版本删除旧版本。像这样: 替代文字

回答by Tom Crockett

If you build your application using Equinoxplugins, you can use the P2 Provisioning Systemto get a ready-made solution to this problem. This will require the server to restart itself after an update.

如果您使用Equinox插件构建您的应用程序,您可以使用P2 Provisioning System获得针对此问题的现成解决方案。这将需要服务器在更新后重新启动。

回答by Peter Knego

This is a known problem and I recommend against reinventing a wheel - don't write your own hack, just use what other people have already done.

这是一个已知问题,我建议不要重新发明轮子 - 不要编写自己的 hack,只需使用其他人已经完成的操作。

Two situations you need to consider:

您需要考虑两种情况:

  1. App needs to be self-updatable and keep running even during update (server app, embedded apps). Go with OSGi: Bundlesor Equinox p2.

  2. App is a desktop app and has an installer. There are many installers with update option. Check installers list.

  1. 应用程序需要可自我更新并在更新期间保持运行(服务器应用程序、嵌入式应用程序)。使用 OSGi: BundlesEquinox p2

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

回答by Berdus

I am currently developing a JAVA Linux Daemon and also had the need to implement an auto-update mechanism. I wanted to limit my application to one jar file, and came up with a simple solution:

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

Pack the updater application in the update itself.

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

Application: When the application detects a newer version it does the following:

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

  1. Download update (Zipfile)
  2. Extract Application and ApplicationUpdater(all in the zipfile)
  3. Run updater
  1. 下载更新(Zipfile)
  2. 提取 Application和 ApplicationUpdater(都在 zipfile 中)
  3. 运行更新程序

ApplicationUpdater: When the updater runs it does the following:

ApplicationUpdater:当更新程序运行时,它会执行以下操作:

  1. Stop the Application (in my case a daemon via init.d)
  2. Copy the downloaded jar file to overwrite current Application
  3. Start the Application
  4. Cleanup.
  1. 停止应用程序(在我的例子中是一个通过 init.d 的守护进程)
  2. 复制下载的 jar 文件以覆盖当前应用程序
  3. 启动应用程序
  4. 清理。

Hope it helps someone.

希望它可以帮助某人。

回答by Kaito

I see a security problem when downloading a new jar (etc.), e.g., a man in the middle attack. You always have to sign your downloadable update.

我在下载新 jar(等)时发现安全问题,例如中间人攻击。您必须始终签署可下载的更新。

On JAX2015, Adam Bien told about using JGit for updating the binaries. Sadly I could not find any tutorials.

在 JAX2015 上,Adam Bien 介绍了使用 JGit 来更新二进制文件。遗憾的是我找不到任何教程。

Source in German.

来源德语。

Adam Bien created the updater see here

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

I forked it herewith some javaFX frontend. I am also working on an automatic signing.

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

回答by Mordechai

I've recently created update4jwhich is fully compatible with Java 9's module system.

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

It will seamlessly start the new version without a restart.

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