Come posso scrivere un'applicazione Java in grado di aggiornarsi in fase di runtime?


91

Vorrei implementare un'applicazione java (applicazione server) in grado di scaricare una nuova versione (file .jar) da un determinato URL e quindi aggiornarsi in fase di esecuzione.

Qual è il modo migliore per farlo ed è possibile?

Immagino che l'applicazione possa scaricare un nuovo file .jar e avviarlo. Ma come devo eseguire la consegna, ad esempio sapere quando viene avviata la nuova applicazione e quindi uscire. O c'è un modo migliore per farlo?


4
Hai esaminato Java Web Start? Tuttavia, credo che non si aggiorni da solo in fase di esecuzione (riavvio richiesto), per questo probabilmente è necessario guardare OSGi.
Thilo

@Thilo: Penso che sarebbe facile scaricare un file da un dato URL e poi avviarlo con un comando linux dal file jar in esecuzione.
Jonas,

1
Il design dell'API WebStart Java rende impossibile l'aggiornamento durante l'esecuzione. Sfortunatamente.
Thorbjørn Ravn Andersen


@meain boss sei forte, mi hai reso la giornata :)
iltaf khalid

Risposte:


68

La struttura di base di una soluzione è la seguente:

  • C'è un ciclo principale responsabile del caricamento ripetuto dell'ultima versione dell'app (se richiesta) e del suo avvio.

  • L'applicazione fa il suo dovere, ma controlla periodicamente l'URL di download. Se rileva una nuova versione, torna al programma di avvio.

Ci sono diversi modi per implementarlo. Per esempio:

  • Il programma di avvio potrebbe essere uno script wrapper o un'applicazione binaria che avvia una nuova JVM per eseguire l'applicazione da un file JAR che viene sostituito.

  • Il launcher potrebbe essere un'applicazione Java che crea un classloader per il nuovo JAR, carica una classe entrypoint e chiama un metodo su di essa. Se lo fai in questo modo, devi controllare le perdite di archiviazione del classloader, ma non è difficile. (Devi solo assicurarti che nessun oggetto con classi caricate dal JAR sia raggiungibile dopo il riavvio.)

I vantaggi dell'approccio del wrapper esterno sono:

  • ti serve solo un JAR,
  • puoi sostituire l'intera app Java,
  • eventuali thread secondari creati dall'app, ecc. andranno via senza una logica di spegnimento speciale e
  • puoi anche gestire il ripristino da arresti anomali dell'applicazione, ecc.

Il secondo approccio richiede due JAR, ma presenta i seguenti vantaggi:

  • la soluzione è Java puro e portabile,
  • il passaggio sarà più rapido e
  • è possibile mantenere più facilmente lo stato durante il riavvio (problemi di perdita del modulo).

Il modo "migliore" dipende dalle tue esigenze specifiche.

Va inoltre notato che:

  • Esistono rischi per la sicurezza con l'aggiornamento automatico. In generale, se il server che fornisce gli aggiornamenti è compromesso o se i meccanismi per fornire gli aggiornamenti sono suscettibili di attacchi, l'aggiornamento automatico può portare a una compromissione dei client.

  • Inviare un aggiornamento a un cliente che causa danni al cliente potrebbe comportare rischi legali e rischi per la reputazione della tua azienda.


Se riesci a trovare un modo per evitare di reinventare la ruota, sarebbe un bene. Vedi le altre risposte per suggerimenti.


43

Attualmente sto sviluppando un demone Linux JAVA e avevo anche la necessità di implementare un meccanismo di aggiornamento automatico. Volevo limitare la mia applicazione a un file jar e ho trovato una soluzione semplice:

Comprimere l'applicazione di aggiornamento nell'aggiornamento stesso.

Applicazione : quando l'applicazione rileva una versione più recente, fa quanto segue:

  1. Scarica aggiornamento (Zipfile)
  2. Estrai Application e ApplicationUpdater (tutto nel file zip)
  3. Esegui il programma di aggiornamento

ApplicationUpdater : quando viene eseguito, il programma di aggiornamento esegue le seguenti operazioni:

  1. Arresta l'applicazione (nel mio caso un demone tramite init.d)
  2. Copia il file jar scaricato per sovrascrivere l'applicazione corrente
  3. Avvia l'applicazione
  4. Pulire.

Spero che aiuti qualcuno.


12

Questo è un problema noto e sconsiglio di reinventare una ruota: non scrivere il tuo hack, usa solo ciò che altre persone hanno già fatto.

Due situazioni che devi considerare:

  1. L'app deve essere autoaggiornabile e continuare a funzionare anche durante l'aggiornamento (app server, app incorporate). Vai con OSGi: Bundles o Equinox p2 .

  2. L'app è un'app desktop e ha un programma di installazione. Ci sono molti programmi di installazione con l'opzione di aggiornamento. Controlla l' elenco degli installatori .


12

Di recente ho creato update4j che è completamente compatibile con il sistema di moduli di Java 9.

Avvierà senza problemi la nuova versione senza un riavvio.


Utilizzando un paradigma bootstrap / applicazione aziendale. Il bootstrap carica e scarica l'applicazione aziendale ed è il bootstrap che generalmente esegue l'aggiornamento.
Mordechai

10

Ho scritto un'applicazione Java in grado di caricare plug-in in fase di esecuzione e iniziare a utilizzarli immediatamente, ispirata a un meccanismo simile in jEdit. jEdit è open source quindi hai la possibilità di vedere come funziona.

La soluzione utilizza un ClassLoader personalizzato per caricare i file dal jar. Una volta caricati, puoi richiamare un metodo dal nuovo jar che fungerà da mainmetodo. Quindi la parte difficile è assicurarsi di eliminare tutti i riferimenti al vecchio codice in modo che possa essere raccolto in modo spazzatura. Non sono proprio un esperto in quella parte, l'ho fatto funzionare ma non è stato facile.


Non so Java, ma in C # puoi usare AppDomains per scaricare esplicitamente il codice. Forse esiste un concetto simile in Java.
Merlyn Morgan-Graham

1
Grazie, questa sembra essere la strada da percorrere. C'è un esempio di a NetworkClassLoadersu JavaDoc per ClassLoader
Jonas

Hai riscontrato problemi con le variabili statiche all'interno della stessa JVM o per quanto riguarda la generazione permanente? Ho sentito che questi possono presentare problemi per quanto riguarda lo scarico delle lezioni.
ide

5
  1. Primo modo: usa Tomcat e le sue strutture di distribuzione.
  2. Secondo modo: dividere l'applicazione in due parti (funzionale e aggiornamento) e lasciare che la parte di aggiornamento sostituisca la parte della funzione.
  3. Terzo modo: nella tua applicazione server basta scaricare la nuova versione, quindi la vecchia versione rilascia la porta vincolata, quindi la vecchia versione esegue la nuova versione (avvia il processo), quindi la vecchia versione invia una richiesta sulla porta dell'applicazione alla nuova versione per eliminare la vecchia versione, vecchia versione termina e la nuova versione cancella la vecchia versione. Come questo: testo alternativo

Il tuo secondo modo sembra essere un design interessante.
Jonas

2

Questo non è necessariamente il modo migliore , ma potrebbe funzionare per te.

Puoi scrivere un'applicazione bootstrap (come il launcher di World of Warcraft, se hai giocato a WoW). Quel bootstrap è responsabile del controllo degli aggiornamenti.

  • Se è disponibile un aggiornamento, lo offrirà all'utente, gestirà il download, l'installazione, ecc.
  • Se l'applicazione è aggiornata, consentirà all'utente di avviare l'applicazione
  • Facoltativamente, puoi consentire all'utente di avviare l'applicazione, anche se non è aggiornata

In questo modo non devi preoccuparti di forzare l'uscita dalla tua applicazione.

Se l'applicazione è basata sul Web ed è importante che disponga di un client aggiornato, è anche possibile eseguire i controlli della versione mentre l'applicazione è in esecuzione. È possibile eseguirle a intervalli, durante la normale comunicazione con il server (alcune o tutte le chiamate) o entrambe.

Per un prodotto su cui ho lavorato di recente, abbiamo eseguito i controlli della versione all'avvio (senza un'app boot strapper, ma prima che apparisse la finestra principale) e durante le chiamate al server. Quando il client non era aggiornato, ci affidavamo all'utente per uscire manualmente, ma proibivamo qualsiasi azione contro il server.

Tieni presente che non so se Java può richiamare il codice dell'interfaccia utente prima di aprire la finestra principale. Stavamo usando C # / WPF.


Grazie, è un bel modo per farlo. Ma è un'applicazione server, quindi non ci sono utenti che possono intraprendere un'azione. E preferirei che fosse solo un singolo file jar, quindi l'utente easyli può scaricare un singolo jar e avviarlo all'inizio, ma dopo non vorrei avere interazioni con l'utente.
Jonas

Quando dici "applicazione server", intendi che si tratta di un'applicazione che viene eseguita direttamente sul server, mentre sei loggato?
Merlyn Morgan-Graham

@ Jonas: Non capisco molto su come funzionano i file .jar, quindi non posso essere di grande aiuto lì. Posso capire se preferisci una risposta specifica per Java. Spero che questo ti dia almeno spunti di riflessione :)
Merlyn Morgan-Graham

@ Merlyn: ti sono grato per aver condiviso le tue idee. Sì, è un'applicazione Java che verrà eseguita su un server Linux e nessuno è connesso a quel server.
Jonas,

1
@ Jonas: Non che non avessi già pensato a questo, ma - La maggior parte dei servizi web che ho visto ha una strategia di distribuzione manuale. Potresti voler stare attento a come controlli gli aggiornamenti. Se si esegue il push up di codice difettoso / danneggiato o si esegue solo parzialmente una distribuzione multi-file, il server potrebbe provare ad aggiornarsi e quindi si avrà un server guasto.
Merlyn Morgan-Graham

2

Se crei la tua applicazione utilizzando i plug-in di Equinox , puoi utilizzare il P2 Provisioning System per ottenere una soluzione pronta per questo problema. Ciò richiederà il riavvio del server dopo un aggiornamento.


1

Vedo un problema di sicurezza durante il download di un nuovo jar (ecc.), Ad esempio un attacco man in the middle. Devi sempre firmare il tuo aggiornamento scaricabile.

Su JAX2015, Adam Bien ha parlato dell'utilizzo di JGit per l'aggiornamento dei binari. Purtroppo non sono riuscito a trovare alcun tutorial.

Fonte in tedesco.

Adam Bien ha creato il programma di aggiornamento vedi qui

L'ho biforcato qui con un frontend javaFX. Sto anche lavorando a una firma automatica.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.