Comment puis-je écrire une application Java qui peut se mettre à jour lors de l'exécution?

91

Je voudrais implémenter une application java (application serveur) qui peut télécharger une nouvelle version (fichier .jar) à partir d'une URL donnée, puis se mettre à jour au moment de l'exécution.

Quelle est la meilleure façon de procéder et est-ce possible?

Je suppose que l'application peut télécharger un nouveau fichier .jar et le démarrer. Mais comment dois-je faire le transfert, par exemple savoir quand la nouvelle application est démarrée puis quitter. Ou y a-t-il une meilleure façon de faire cela?

Jonas
la source
4
Avez-vous regardé Java Web Start? Il ne se met pas à jour au moment de l'exécution cependant, je crois, (redémarrage requis), pour cela, vous devez probablement regarder OSGi.
Thilo
@Thilo: Je pense qu'il serait facile de télécharger un fichier à partir d'une URL donnée, puis de le démarrer avec une commande linux à partir du fichier jar en cours d'exécution.
Jonas
1
La conception de l'API Java WebStart rend impossible la mise à jour en cours d'exécution. Malheureusement.
Thorbjørn Ravn Andersen
@meain boss you rock, you made my day :)
iltaf khalid

Réponses:

67

La structure de base d'une solution est la suivante:

  • Il existe une boucle principale chargée de charger à plusieurs reprises la dernière version de l'application (si nécessaire) et de la lancer.

  • L'application fait son travail, mais vérifie périodiquement l'URL de téléchargement. S'il détecte une nouvelle version, il revient au lanceur.

Vous pouvez mettre cela en œuvre de plusieurs manières. Par exemple:

  • Le lanceur peut être un script wrapper ou une application binaire qui démarre une nouvelle JVM pour exécuter l'application à partir d'un fichier JAR qui est remplacé.

  • Le lanceur peut être une application Java qui crée un chargeur de classe pour le nouveau JAR, charge une classe de point d'entrée et appelle une méthode dessus. Si vous procédez de cette façon, vous devez surveiller les fuites de stockage du chargeur de classe, mais ce n'est pas difficile. (Vous devez simplement vous assurer qu'aucun objet avec des classes chargées à partir du JAR n'est accessible après le redémarrage.)

Les avantages de l'approche du wrapper externe sont:

  • vous n'avez besoin que d'un JAR,
  • vous pouvez remplacer toute l'application Java,
  • tous les threads secondaires créés par l'application, etc. disparaîtront sans logique d'arrêt spéciale, et
  • vous pouvez également gérer la récupération après des plantages d'applications, etc.

La seconde approche nécessite deux JAR, mais présente les avantages suivants:

  • la solution est purement Java et portable,
  • le changement sera plus rapide et
  • vous pouvez plus facilement conserver l'état tout au long du redémarrage (problèmes de fuite modulo).

Le "meilleur" moyen dépend de vos besoins spécifiques.

Il convient également de noter que:

  • La mise à jour automatique comporte des risques de sécurité. En général, si le serveur qui fournit les mises à jour est compromis, ou si les mécanismes de fourniture des mises à jour sont susceptibles d'être attaqués, la mise à jour automatique peut conduire à une compromission du ou des clients.

  • Envoyer une mise à jour à un client qui cause des dommages au client peut comporter des risques juridiques et des risques pour la réputation de votre entreprise.


Si vous pouvez trouver un moyen d'éviter de réinventer la roue, ce serait bien. Voir les autres réponses pour des suggestions.

Stephen C
la source
43

Je développe actuellement un démon Linux JAVA et j'ai également eu besoin de mettre en œuvre un mécanisme de mise à jour automatique. Je voulais limiter mon application à un fichier jar et j'ai trouvé une solution simple:

Emballez l'application de mise à jour dans la mise à jour elle-même.

Application : lorsque l'application détecte une version plus récente, elle effectue les opérations suivantes:

  1. Télécharger la mise à jour (Zipfile)
  2. Extraire Application et ApplicationUpdater (le tout dans le fichier zip)
  3. Exécuter le programme de mise à jour

ApplicationUpdater : lorsque le programme de mise à jour s'exécute, il effectue les opérations suivantes:

  1. Arrêtez l'application (dans mon cas, un démon via init.d)
  2. Copiez le fichier jar téléchargé pour écraser l'application actuelle
  3. Démarrez l'application
  4. Nettoyer.

J'espère que ça aide quelqu'un.

Berdus
la source
12

C'est un problème connu et je recommande de ne pas réinventer une roue - n'écrivez pas votre propre hack, utilisez simplement ce que les autres ont déjà fait.

Deux situations à considérer:

  1. L'application doit être auto-actualisable et continuer à fonctionner même pendant la mise à jour (application serveur, applications intégrées). Optez pour OSGi: Bundles ou Equinox p2 .

  2. L'application est une application de bureau et dispose d'un programme d'installation. Il existe de nombreux installateurs avec option de mise à jour. Vérifiez la liste des installateurs .

Peter Knego
la source
12

J'ai récemment créé update4j qui est entièrement compatible avec le système de modules de Java 9.

Il démarrera de manière transparente la nouvelle version sans redémarrage.

Mordechai
la source
En utilisant un paradigme bootstrap / application métier. Le bootstrap charge et décharge l'application métier et c'est le bootstrap qui effectue généralement la mise à jour.
Mordechai le
10

J'ai écrit une application Java qui peut charger des plugins lors de l'exécution et commencer à les utiliser immédiatement, inspirée d'un mécanisme similaire dans jEdit. jEdit est open source, vous avez donc la possibilité de voir comment cela fonctionne.

La solution utilise un ClassLoader personnalisé pour charger des fichiers à partir du fichier jar. Une fois qu'ils sont chargés, vous pouvez appeler une méthode du nouveau jar qui agira comme sa mainméthode. Ensuite, la partie la plus délicate est de vous assurer de vous débarrasser de toutes les références à l'ancien code afin qu'il puisse être récupéré. Je ne suis pas tout à fait un expert dans ce domaine, je l'ai fait fonctionner mais ce n'était pas facile.

Brad Mace
la source
Je ne connais pas Java, mais en C #, vous pouvez utiliser AppDomains pour décharger explicitement le code. Il existe peut-être un concept similaire en Java.
Merlyn Morgan-Graham
1
Merci, cela semble être la voie à suivre. Il y a un exemple de NetworkClassLoadersur le JavaDoc pour ClassLoader
Jonas
Avez-vous rencontré des problèmes avec des variables statiques au sein de la même JVM, ou qu'en est-il de la génération permanente? J'ai entendu dire que cela peut poser des problèmes en ce qui concerne le déchargement de classe.
Id
5
  1. Première façon: utilisez tomcat et déployez des installations.
  2. Deuxième façon: diviser l'application en deux parties (fonctionnelle et mise à jour) et laisser la partie mise à jour remplacer la partie fonction.
  3. Troisième façon: dans votre application serveur, téléchargez simplement la nouvelle version, puis l'ancienne version libère le port lié, puis l'ancienne version exécute la nouvelle version (démarre le processus), puis l'ancienne version envoie une demande sur le port d'application à la nouvelle version pour supprimer l'ancienne version, l'ancienne version se termine et la nouvelle version supprime l'ancienne version. Comme ça: texte alternatif
jnr
la source
Votre deuxième moyen semble être une conception intéressante.
Jonas
2

Ce n'est pas nécessairement la meilleure façon, mais cela pourrait fonctionner pour vous.

Vous pouvez écrire une application de bootstrap (ala le lanceur World of Warcraft, si vous avez joué à WoW). Ce bootstrap est responsable de la vérification des mises à jour.

  • Si une mise à jour est disponible, elle la proposera à l'utilisateur, s'occupera du téléchargement, de l'installation, etc.
  • Si l'application est à jour, elle permettra à l'utilisateur de lancer l'application
  • En option, vous pouvez autoriser l'utilisateur à lancer l'application, même si elle n'est pas à jour

De cette façon, vous n'avez pas à vous soucier de forcer la sortie de votre application.

Si votre application est basée sur le Web et s'il est important qu'elle dispose d'un client à jour, vous pouvez également effectuer des vérifications de version pendant l'exécution de l'application. Vous pouvez les faire à intervalles, tout en effectuant une communication normale avec le serveur (certains ou tous les appels), ou les deux.

Pour un produit sur lequel j'ai récemment travaillé, nous avons effectué des vérifications de version au lancement (sans application de boot strapper, mais avant l'apparition de la fenêtre principale), et lors des appels au serveur. Lorsque le client était obsolète, nous comptions sur l'utilisateur pour quitter manuellement, mais nous interdisons toute action contre le serveur.

Veuillez noter que je ne sais pas si Java peut invoquer le code de l'interface utilisateur avant d'afficher votre fenêtre principale. Nous utilisions C # / WPF.

Merlyn Morgan-Graham
la source
Merci, c'est une belle façon de le faire. Mais c'est une application serveur, donc aucun utilisateur ne peut entreprendre une action. Et je préférerais qu'il ne s'agisse que d'un seul fichier jar, de sorte que l'utilisateur easyli puisse télécharger un seul fichier et le démarrer au début, mais après cela, je ne voudrais plus avoir d'interaction avec l'utilisateur.
Jonas
Lorsque vous dites "application serveur", voulez-vous dire que c'est une application qui s'exécute directement sur le serveur, une fois connecté?
Merlyn Morgan-Graham
@Jonas: Je ne comprends pas grand-chose au fonctionnement des fichiers .jar, donc je ne peux pas être d'une grande utilité là-bas. Je peux comprendre si vous préférez une réponse spécifique à Java. J'espère que cela vous donnera matière à réflexion, au moins :)
Merlyn Morgan-Graham
@Merlyn: Je suis reconnaissant d'avoir partagé vos idées. Oui, c'est une application Java qui s'exécutera sur un serveur Linux, et personne n'est connecté sur ce serveur.
Jonas
1
@Jonas: Non pas que vous n'y aviez pas déjà pensé, mais - La plupart des services Web que j'ai vus ont une stratégie de déploiement manuel. Vous voudrez peut-être faire attention à la manière dont vous recherchez les mises à jour. Si vous faites remonter du code bogué / cassé, ou si vous ne faites qu'en partie avec un déploiement multi-fichiers, le serveur pourrait essayer de se mettre à jour, et vous auriez alors un serveur cassé.
Merlyn Morgan-Graham
2

Si vous créez votre application à l'aide des plug-ins Equinox , vous pouvez utiliser le système de provisionnement P2 pour obtenir une solution toute faite à ce problème. Cela obligera le serveur à redémarrer lui-même après une mise à jour.

Tom Crockett
la source
1

Je vois un problème de sécurité lors du téléchargement d'un nouveau fichier jar (etc.), par exemple, un homme au milieu attaque. Vous devez toujours signer votre mise à jour téléchargeable.

Sur JAX2015, Adam Bien a parlé de l'utilisation de JGit pour mettre à jour les binaires. Malheureusement, je n'ai trouvé aucun tutoriel.

Source en allemand.

Adam Bien a créé le programme de mise à jour voir ici

Je l'ai bifurqué ici avec une interface javaFX. Je travaille également sur une signature automatique.

Kaito
la source