Wie kann ich eine Java-Anwendung schreiben, die sich zur Laufzeit selbst aktualisieren kann?

90

Ich möchte eine Java-Anwendung (Serveranwendung) implementieren, die eine neue Version (.jar-Datei) von einer bestimmten URL herunterladen und sich dann zur Laufzeit selbst aktualisieren kann.

Was ist der beste Weg dies zu tun und ist es möglich?

Ich denke, dass die Anwendung eine neue JAR-Datei herunterladen und starten kann. Aber wie soll ich die Übergabe machen, zB wissen, wann die neue Anwendung gestartet wird und dann beenden. Oder gibt es einen besseren Weg, dies zu tun?

Jonas
quelle
4
Haben Sie sich Java Web Start angesehen? Es aktualisiert sich jedoch nicht zur Laufzeit, glaube ich, (Neustart erforderlich), dafür müssen Sie sich wahrscheinlich OSGi ansehen.
Thilo
@Thilo: Ich denke, es wäre einfach, eine Datei von einer bestimmten URL herunterzuladen und sie dann mit einem Linux-Befehl aus der laufenden JAR-Datei zu starten.
Jonas
1
Das Design der Java WebStart-API macht eine Aktualisierung während der Ausführung unmöglich. Unglücklicherweise.
Thorbjørn Ravn Andersen
@meain Chef du rockst, du hast meinen Tag gemacht :)
iltaf khalid

Antworten:

67

Die Grundstruktur einer Lösung ist wie folgt:

  • Es gibt eine Hauptschleife, die dafür verantwortlich ist, die neueste Version der App (falls erforderlich) wiederholt zu laden und zu starten.

  • Die Anwendung macht ihre Sache, überprüft aber regelmäßig die Download-URL. Wenn eine neue Version erkannt wird, kehrt sie zum Launcher zurück.

Es gibt verschiedene Möglichkeiten, dies zu implementieren. Beispielsweise:

  • Der Launcher kann ein Wrapper-Skript oder eine Binäranwendung sein, die eine neue JVM startet, um die Anwendung aus einer JAR-Datei auszuführen, die ersetzt wird.

  • Der Launcher kann eine Java-Anwendung sein, die einen Klassenladeprogramm für die neue JAR erstellt, eine Einstiegspunktklasse lädt und eine Methode dafür aufruft. Wenn Sie dies auf diese Weise tun, müssen Sie auf Speicherlecks im Classloader achten, aber das ist nicht schwierig. (Sie müssen nur sicherstellen, dass nach dem Neustart keine Objekte mit aus der JAR geladenen Klassen erreichbar sind.)

Die Vorteile des externen Wrapper-Ansatzes sind:

  • Sie brauchen nur ein Glas,
  • Sie können die gesamte Java-App ersetzen.
  • Alle von der App usw. erstellten sekundären Threads werden ohne spezielle Abschaltlogik entfernt
  • Sie können sich auch mit der Wiederherstellung nach Anwendungsabstürzen usw. befassen.

Der zweite Ansatz erfordert zwei JARs, hat jedoch die folgenden Vorteile:

  • Die Lösung ist reines Java und portabel.
  • Die Umstellung erfolgt schneller und
  • Sie können den Status während des Neustarts einfacher beibehalten (Modulo-Leckageprobleme).

Der "beste" Weg hängt von Ihren spezifischen Anforderungen ab.

Es sollte auch beachtet werden, dass:

  • Bei der automatischen Aktualisierung bestehen Sicherheitsrisiken. Wenn der Server, der die Updates bereitstellt, gefährdet ist oder wenn die Mechanismen zum Bereitstellen der Updates anfällig für Angriffe sind, kann die automatische Aktualisierung im Allgemeinen zu einer Gefährdung der Clients führen.

  • Das Weiterleiten eines Updates an einen Kunden, das dem Kunden Schaden zufügt, kann rechtliche Risiken und Risiken für den Ruf Ihres Unternehmens mit sich bringen.


Wenn Sie einen Weg finden, das Rad nicht neu zu erfinden, wäre das gut. In den anderen Antworten finden Sie Vorschläge.

Stephen C.
quelle
43

Ich entwickle derzeit einen JAVA Linux Daemon und musste auch einen Mechanismus zur automatischen Aktualisierung implementieren. Ich wollte meine Anwendung auf eine JAR-Datei beschränken und fand eine einfache Lösung:

Packen Sie die Updater-Anwendung in das Update selbst.

Anwendung : Wenn die Anwendung eine neuere Version erkennt, führt sie Folgendes aus:

  1. Update herunterladen (Zipfile)
  2. Anwendung und ApplicationUpdater extrahieren (alle in der Zip-Datei)
  3. Führen Sie den Updater aus

ApplicationUpdater : Wenn der Updater ausgeführt wird, führt er Folgendes aus:

  1. Stoppen Sie die Anwendung (in meinem Fall ein Daemon über init.d)
  2. Kopieren Sie die heruntergeladene JAR-Datei, um die aktuelle Anwendung zu überschreiben
  3. Starten Sie die Anwendung
  4. Aufräumen.

Hoffe es hilft jemandem.

Berdus
quelle
12

Dies ist ein bekanntes Problem, und ich empfehle, ein Rad nicht neu zu erfinden. Schreiben Sie keinen eigenen Hack, sondern verwenden Sie nur das, was andere bereits getan haben.

Zwei Situationen, die Sie berücksichtigen müssen:

  1. Die App muss selbst aktualisierbar sein und auch während des Updates ausgeführt werden (Server-App, eingebettete Apps). Gehen Sie mit OSGi: Bundles oder Equinox p2 .

  2. App ist eine Desktop-App und hat ein Installationsprogramm. Es gibt viele Installationsprogramme mit Update-Option. Überprüfen Sie die Installationsliste .

Peter Knego
quelle
12

Ich habe kürzlich update4j erstellt, das vollständig mit dem Modulsystem von Java 9 kompatibel ist.

Die neue Version wird nahtlos ohne Neustart gestartet.

Mordechai
quelle
Durch Verwendung eines Bootstrap- / Geschäftsanwendungsparadigmas. Der Bootstrap lädt und entlädt die Geschäftsanwendung und es ist der Bootstrap, der im Allgemeinen das Update durchführt.
Mordechai
10

Ich habe eine Java-Anwendung geschrieben, die Plugins zur Laufzeit laden und sofort verwenden kann, inspiriert von einem ähnlichen Mechanismus in jEdit. jEdit ist Open Source, sodass Sie die Möglichkeit haben, zu sehen, wie es funktioniert.

Die Lösung verwendet einen benutzerdefinierten ClassLoader, um Dateien aus dem JAR zu laden. Sobald sie geladen sind, können Sie eine Methode aus dem neuen Jar aufrufen, die als mainMethode fungiert. Dann besteht der schwierige Teil darin, sicherzustellen, dass Sie alle Verweise auf den alten Code entfernen, damit dieser durch Müll gesammelt werden kann. Ich bin kein Experte in diesem Bereich, ich habe es geschafft, aber es war nicht einfach.

Brad Mace
quelle
Ich weiß nichts über Java, aber in C # können Sie AppDomains verwenden, um Code explizit zu entladen. Vielleicht gibt es in Java ein ähnliches Konzept.
Merlyn Morgan-Graham
1
Danke, das scheint der richtige Weg zu sein. Es gibt ein Beispiel für eine NetworkClassLoaderauf dem JavaDoc für ClassLoader
Jonas
Haben Sie Probleme mit statischen Variablen innerhalb derselben JVM oder was ist mit der permanenten Generierung? Ich habe gehört, dass dies Probleme beim Entladen von Klassen aufwerfen kann.
Ide
5
  1. Erster Weg: Verwenden Sie Tomcat und seine Bereitstellungsfunktionen.
  2. Zweiter Weg: Aufteilen der Anwendung auf zwei Teile (Funktion und Aktualisierung) und Ersetzen des Funktionsteils durch das Aktualisierungsteil.
  3. Dritter Weg: Laden Sie in Ihrer Serveranwendung einfach eine neue Version herunter, dann gibt die alte Version den gebundenen Port frei, dann führt die alte Version eine neue Version aus (startet den Prozess), und die alte Version sendet eine Anfrage am Anwendungsport an die neue Version, um die alte Version und die alte Version zu löschen wird beendet und neue Version löscht alte Version. So was: Alt-Text
jnr
quelle
Ihr zweiter Weg scheint ein interessantes Design zu sein.
Jonas
2

Dies ist nicht unbedingt der beste Weg, aber es könnte für Sie funktionieren.

Sie können eine Bootstrap-Anwendung schreiben (ua der World of Warcraft-Launcher, wenn Sie WoW gespielt haben). Dieser Bootstrap ist für die Suche nach Updates verantwortlich.

  • Wenn ein Update verfügbar ist, bietet es es dem Benutzer an, übernimmt den Download, die Installation usw.
  • Wenn die Anwendung auf dem neuesten Stand ist, kann der Benutzer die Anwendung starten
  • Optional können Sie dem Benutzer erlauben, die Anwendung zu starten, auch wenn sie nicht aktuell ist

Auf diese Weise müssen Sie sich keine Sorgen machen, dass Ihre Anwendung beendet werden muss.

Wenn Ihre Anwendung webbasiert ist und es wichtig ist, dass sie über einen aktuellen Client verfügt, können Sie auch Versionsprüfungen durchführen, während die Anwendung ausgeführt wird. Sie können sie in Intervallen ausführen, während Sie eine normale Kommunikation mit dem Server durchführen (einige oder alle Anrufe) oder beides.

Für ein Produkt, an dem ich kürzlich gearbeitet habe, haben wir beim Start (ohne Bootstrapper-App, aber bevor das Hauptfenster angezeigt wurde) und bei Aufrufen des Servers Versionsprüfungen durchgeführt. Wenn der Client veraltet war, haben wir uns darauf verlassen, dass der Benutzer manuell beendet, aber keine Aktionen gegen den Server ausgeführt.

Bitte beachten Sie, dass ich nicht weiß, ob Java UI-Code aufrufen kann, bevor Sie Ihr Hauptfenster aufrufen. Wir haben C # / WPF verwendet.

Merlyn Morgan-Graham
quelle
Danke, das ist eine schöne Art, es zu tun. Da es sich jedoch um eine Serveranwendung handelt, gibt es keinen Benutzer, der Maßnahmen ergreifen kann. Und ich würde es vorziehen, dass es nur eine einzelne JAR-Datei ist, so dass der Benutzer easyli eine einzelne JAR-Datei herunterladen und am Anfang starten kann, aber danach möchte ich keine Benutzerinteraktionen mehr haben.
Jonas
Wenn Sie "Serveranwendung" sagen, meinen Sie damit, dass es sich um eine Anwendung handelt, die direkt auf dem Server ausgeführt wird, während Sie angemeldet sind?
Merlyn Morgan-Graham
@ Jonas: Ich verstehe nicht viel darüber, wie .jar-Dateien funktionieren, daher kann ich dort nicht viel helfen. Ich kann verstehen, ob Sie eine Java-spezifische Antwort bevorzugen. Hoffentlich gibt Ihnen dies zumindest Anlass zum Nachdenken :)
Merlyn Morgan-Graham
@ Merlyn: Ich bin dankbar, dass du deine Ideen geteilt hast. Ja, es ist eine Java-Anwendung, die auf einem Linux-Server ausgeführt wird, und auf diesem Server ist niemand angemeldet.
Jonas
1
@ Jonas: Nicht, dass Sie noch nicht darüber nachgedacht hätten, aber - Die meisten Webdienste, die ich gesehen habe, haben eine manuelle Bereitstellungsstrategie. Möglicherweise möchten Sie vorsichtig sein, wie Sie nach Updates suchen. Wenn Sie fehlerhaften / defekten Code hochschieben oder nur teilweise mit einer Bereitstellung mit mehreren Dateien fertig werden, versucht der Server möglicherweise, sich selbst zu aktualisieren, und dann haben Sie einen defekten Server.
Merlyn Morgan-Graham
2

Wenn Sie Ihre Anwendung mit Equinox- Plugins erstellen, können Sie das P2-Bereitstellungssystem verwenden , um eine vorgefertigte Lösung für dieses Problem zu erhalten. Dazu muss sich der Server nach einem Update neu starten.

Tom Crockett
quelle
1

Ich sehe ein Sicherheitsproblem beim Herunterladen eines neuen Glases (usw.), z. B. eines Mannes im mittleren Angriff. Sie müssen Ihr herunterladbares Update immer unterschreiben.

Auf JAX2015 erzählte Adam Bien von der Verwendung von JGit zum Aktualisieren der Binärdateien. Leider konnte ich keine Tutorials finden.

Quelle in deutscher Sprache.

Adam Bien hat den Updater erstellt, siehe hier

Ich habe es hier mit einem JavaFX-Frontend gegabelt . Ich arbeite auch an einer automatischen Signatur.

Kaito
quelle